跨域问题
content-type 描述前端发送的数据格式
推荐博客:https://blog.csdn.net/danielzhou888/article/details/72861097 简单请求 http : HEAD GET POST Content-Type只能是下列类型中的一个 application/x-www-from-urlencoded form表单请求 multipart/form-data 需要在表单中进行文件上传时,就需要使用该格式 text/plain 纯文本格式 复杂请求 只要不满足简单请求都是复杂请求 复杂请求先发送预检 OPTIONS
利用中间件加响应头解决跨域
# 中间件: from django.utils.deprecation import MiddlewareMixin class CorsMiddleWare(MiddlewareMixin): def process_response(self, request, response): # 简单请求,加个响应头就可以了 response["Access-Control-Allow-Origin"] = "*" # 允许所有的源(针对的是浏览器的同源策略) if request.method == "OPTIONS": response["Access-Control-Allow-Headers"] = "Content-Type,a,token" # 当是预检请求时,允许携带Content-Type,a,token请求头 # content-type允许的是请求头中的Contend-Type键值对, # 简单请求只允许application/x-www-from-urlencoded、multipart/form-data、text/plain这三种类型的请求, # 当前端发送json请求时,如果不设置content-type就不被允许 # a ,token请求头时我们在前端发送axios自定义的请求头 response["Access-Control-Allow-Methods"] = "POST, PUT, PATCH, DELETE" # 除了简单请求外这些请求方式也都允许 return response
# views.py文件 from course.utils.authentication import MyAuth class CourseView(ModelViewSet): authentication_classes = [MyAuth, ] # 进行局部认证,所有走这个视图函数的都需要认证,在我们这里是都要携带token请求头 queryset = Course.objects.all() serializer_class = CourseModelSerializer def list(self, request, *args, **kwargs): print(request.META.get('HTTP_A')) print(request.META.get('HTTP_TOKEN')) queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) response={"code":1000,"data":None,"err_msg":""} if page is not None: serializer = self.get_serializer(page, many=True) response["data"]=serializer.data return self.get_paginated_response(response) serializer = self.get_serializer(queryset, many=True) response["data"] = serializer.data return Response(response)
# authentication.py 自己写的认证组件 from rest_framework import authentication from rest_framework.exceptions import AuthenticationFailed from course.models import Userinfo, UserToken class MyAuth(authentication.BaseAuthentication): def authenticate(self, request): if request.method == "OPTIONS": # 复杂请求走预检时,直接返回,走CorsMiddleWare中间件中的process_response,给响应加允许的头 return token = request.META.get('HTTP_TOKEN') # 不是复杂请求时,从request.META拿到请求头携带的数据,django会对我们的请求头做处理,前端axios发送的请求头是token,django拿到的请求头就会变成HTTP_TOKEN,而且这里的request也是restframework重新封装后的request user_obj = UserToken.objects.filter(token=token).first() # 获取前端传过来的token if not user_obj: raise AuthenticationFailed("无效的token") return user_obj.user, token
# 前端axios请求文件 category_click_all: function () { this.current = -1; let that = this; // console.log(this.$store.state.token,'token已经从仓库中拿到了) this.$axios.request({ url: "http://127.0.0.1:8000/course/", //请求地址 method: "get", headers: { a: '1', token: this.$store.state.token, //携带的请求头 }, }).then(function (res) { console.log("res:", res); that.course_list = res.data.data }).catch(function (data) { console.log(data) }) },
#浏览器跨域: 1,先走预检请求 request.method == "OPTIONS",这和get post是同级别的请求 2,vue发axios携带请求头时,get请求,这是第二次请求 当没有认证组件时,第一次请求在视图函数中没有找到相应的函数来处理options请求,最后走中间件,在response响应头中添加了允许的请求头和所有的源,表示允许携带请求头 第二次请求是get请求,视图函数可以进行处理,可以在request.META中拿到请求头,当然最后走中间件process_response 当有认证组件时,第一次请求到来先走认证组件,在认证组件中进行判断,如果是options请求return,这是执行中间件的process_response方法,响应预检请求 第二次请求get请求,这是就可以拿到request.META中的请求头数据了