zoukankan      html  css  js  c++  java
  • python 全栈开发,Day100(restful 接口,DRF组件,DRF跨域(cors组件))

    昨日内容回顾

    1. 为什么要做前后端分离?
        - 前后端交给不同的人来编写,职责划分明确。方便快速开发
        - 针对pc,手机,ipad,微信,支付宝... 使用同一个接口
        
    2. 简述http协议?
        - 基于socket
        - 数据格式:
            "GET /index?name=123&age=19 http1.1
    host:www.luffyciti.com
    content-type:application/json...
    
    "
            
            "POST /index http1.1
    host:www.luffyciti.com
    content-type:application/json...
    
    {name:'alex',age:18}"
            
            "POST /index http1.1
    host:www.luffyciti.com
    content-type:application/enform.....
    
    name=alex&age=18&xx=19"
        - 无状态短链接
            一次请求一次响应之后断开连接
            
    3. 简述restful 规范?
        https://www.luffycity.com/api/v1/courses/?sub_category=0
        看上面一段url,可以说出5个
        1. 使用https代替http  2.在URL中体现自己写的是API 3. 在URL中体现版本  4. 使用名词 5.参数要合理
        之后,请求方式,响应信息。可以说后面5个
        6. 根据请求方式不同,处理不同的操作 7.
        
        
    4. django rest framework组件的作用?
        - 快速实现restful 规范
            
    5. 列举django rest framework组件(10)?
        
    6. 路飞的表结构
    View Code

    一、restful 接口

    根据昨天代码,继续编写。

    或者下载代码:

    https://github.com/987334176/luffycity/archive/v1.2.zip

    注意:删除api目录下的views.py文件,它没有用了

    下载数据库使用:

    https://github.com/987334176/luffycity/blob/master/db.sqlite3

    第一种方式

    修改api_urls.py

    from django.conf.urls import url
    from api.views import course,degreecourse
    
    urlpatterns = [
        # url(r'login/$', views.LoginView.as_view()),
        url(r'courses/$',course.CoursesView.as_view()),
        url(r'courses/(?P<pk>d+)/$',course.CourseDetailView.as_view()),
    
        url(r'degreecourse/$',degreecourse.DegreeCourseView.as_view()),
        url(r'degreecourse/teachers/$',degreecourse.DegreeCourseTeachersView.as_view()),
        url(r'degreecourse/scholarship/$',degreecourse.DegreeCourseScholarshipView.as_view()),
    
    ]
    View Code

    修改views目录下的course.py

    from rest_framework.views import APIView
    from rest_framework.response import Response
    
    from api import models
    from api.serializers.course import CourseModelSerializer
    
    from api.utils.response import BaseResponse
    from api.utils.serialization_general import SerializedData
    
    
    class CoursesView(APIView):
    
        def get(self, request, *args, **kwargs):
            """
            查询所有
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
            queryset = models.Course.objects.all()
            serializer_class = CourseModelSerializer
            data = SerializedData(request, queryset, serializer_class).get_data()
            return Response(data)
    
        def post(self, request, *args, **kwargs):
            """
            增加一条
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
    
    class CourseDetailView(APIView):
        def get(self, request, pk,*args, **kwargs):
            """
            查询单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def put(self, request, pk,*args, **kwargs):
            """
            修改单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def delete(self, request, pk,*args, **kwargs):
            """
            删除单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
    View Code

    上面的方式,更趋近于原始

    第二种方式(推荐)

    django-rst-framework为我们提供了ViewSet类, ViewSet为我们提供了默认的URL结构, 使得我们能更专注于API本身. ViewSet类与View类几乎是相同的, 但提供的是read或update, 而不是http动作get或put.

    ViewSet类在实例化后, 通过Router类, 最终将URL与ViewSet方法绑定

    ViewSet类的父类ViewSetMixin,其实重写了as_view方法

    @classonlymethod
        def as_view(cls, actions=None, **initkwargs):
            """
            Because of the way class based views create a closure around the
            instantiated view, we need to totally reimplement `.as_view`,
            and slightly modify the view function that is created and returned.
            """
            ...

    那么url方式也发生了变化

    修改api_urls.py

    from django.conf.urls import url
    from api.views import course
    
    urlpatterns = [
        url(r'courses/$', course.CoursesView.as_view({'get': 'list', 'post': 'create'})),
        url(r'courses/(?P<pk>d+)/$', course.CoursesView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
    ]
    View Code

    解释:看这一段{'get':'list','post':'create'}

    它表示get请求时,转发到list方法。post请求转发到create方法。

    这里面定义的方法名,都是约定俗成的。推荐使用这些名字!也可以自定义。

    修改views目录下的course.py

    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.response import Response
    
    from api import models
    from api.serializers.course import CourseModelSerializer
    
    from api.utils.response import BaseResponse
    from api.utils.serialization_general import SerializedData
    
    
    class CoursesView(ViewSetMixin,APIView):
    
        def list(self, request, *args, **kwargs):
            """
            查询所有
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
            queryset = models.Course.objects.all()
            serializer_class = CourseModelSerializer
            data = SerializedData(request, queryset, serializer_class).get_data()
            return Response(data)
    
        def create(self, request, *args, **kwargs):
            """
            增加一条
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
    
    class CourseDetailView(APIView):
        def retrieve(self, request, pk,*args, **kwargs):
            """
            查询单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def update(self, request, pk,*args, **kwargs):
            """
            修改单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def destroy(self, request, pk,*args, **kwargs):
            """
            删除单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
    View Code

    以上方式适用于:用户请求处理时业务逻辑复杂的情况。

    注意:如果继承了GenericViewSet,并且返回序列化数据使用了Response

    必须要定义queryset属性,否则报错

    看源码

     

    必须要定义queryset变量,否则执行assert(断言)。

    还有一种解决方式,使用JSONRenderer。它也可以返回json数据,但是没有像Response那样,有好看的页面!

    三、DRF跨域(cors组件)

    运行的vue项目,发现出现跨域

    查看Pycharm控制台输出:

    [18/Aug/2018 20:14:31] "GET /api/v1/courses/ HTTP/1.1" 200 212

    可以发现,请求实际已经到达了后端。并且做了正确的返回,但是浏览器拒绝接收!

    这为什么呢?这是因为浏览器的同源策略

    1.什么是同源策略

    同源: 页面地址拥有相同的协议,域名,以及端口号。
     
    同源策略: 页面A中的脚本不能访问不同源的页面B的cookie、localStorage以及IndexDB,也不能获取页面B的DOM信息, 另外在使用AJAX时也会受到相关限制。

    2.同源策略的目的

    主要是出于安全方面的考虑。现在的网页都用cookie来进行身份验证,如果不限制读取,网页B里的恶意脚本代码可以随意模仿真实用户进行操作。

    关于更多的同源策略信息,请参考链接:

    http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

    总结:

    浏览器具有同源策略,打开某个网站后,通过ajax向另外一个网站发送http请求时候,数据回来时会被浏览器阻止(跨域)

    解决跨域

    方式一

    在api端设置响应头 CORS

    修改views目录下的course.py,增加响应头

    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.response import Response
    
    from api import models
    from api.serializers.course import CourseModelSerializer
    
    from api.utils.response import BaseResponse
    from api.utils.serialization_general import SerializedData
    
    
    class CoursesView(ViewSetMixin,APIView):
    
        def list(self, request, *args, **kwargs):
            """
            查询所有
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
            queryset = models.Course.objects.all()
            serializer_class = CourseModelSerializer
            data = SerializedData(request, queryset, serializer_class).get_data()
            # 增加响应头,http://localhost:8080表示前端的地址
            return Response(data,headers={'Access-Control-Allow-Origin': 'http://localhost:8080'})
    
        def create(self, request, *args, **kwargs):
            """
            增加一条
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
    
    class CourseDetailView(APIView):
        def retrieve(self, request, pk,*args, **kwargs):
            """
            查询单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def update(self, request, pk,*args, **kwargs):
            """
            修改单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
        def destroy(self, request, pk,*args, **kwargs):
            """
            删除单个
            :param request:
            :param pk:
            :param args:
            :param kwargs:
            :return:
            """
    View Code

    Access-Control-Allow-Origin字段,表示http://localhost:8080可以请求数据。该字段也可以设为星号,表示同意任意跨源请求

    客户端浏览器检查自己的域是否在允许列表中,决定是否处理响应

    刷新vue页面,发现没有报错了。说明接收了数据!

    上面是简单请求

    简单请求 OR 非简单请求

    条件:

    1、请求方式:HEAD、GET、POST
    2、请求头信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 对应的值是以下三个中的任意一个
            application/x-www-form-urlencoded
            multipart/form-data
            text/plain

    注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

    简单请求和非简单请求的区别

    简单请求:一次请求
    非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用

    关于"预检"

    - 请求方式:OPTIONS
    - "预检"其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
    - 如何"预检"
         => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则"预检"不通过
            Access-Control-Request-Method
         => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则"预检"不通过
            Access-Control-Request-Headers        text/plain

    数据展示

    修改settings.py,将分页改成20

    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
        'VERSION_PARAM':'version',
        'DEFAULT_VERSION':'v1',
        'ALLOWED_VERSIONS':['v1','v2'],
        'PAGE_SIZE':20,
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    }
    View Code

    修改vue项目的HelloWorld.vue

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <h1>课程列表</h1>
        <ul v-for="item in courseList">
          <li>{{item.name}}</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      data () {
        return {
          msg: '欢迎使用路飞学城',
          courseList:[],
        }
      },
      mounted(){  //页面加载完成后
        this.initCourse();  //执行此方法
      },
      methods:{
        initCourse:function () {
          let that = this
          //向后台发送ajax请求
          this.$axios.request({
            url:'http://127.0.0.1:8000/api/v1/courses/',
            method:'GET',
            responseType:'json',
          }).then(function (arg) {
            //成功之后
            console.log(arg);
            if (arg.data.code == 1000){
              //更新数据
              that.courseList = arg.data.data;
            }else {
              //弹出错误
              alert(arg.data.error);
            }
    
          }).catch(function (err) {
            //发生错误
            console.log(err);
          })
        }
      },
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>
    View Code

    刷新网页,效果如下:

    第二种

    使用自定义中间件

    修改views目录下的course.py,去掉headers

        def list(self, request, *args, **kwargs):
            """
            查询所有
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
            queryset = models.Course.objects.all()
            serializer_class = CourseModelSerializer
            data = SerializedData(request, queryset, serializer_class).get_data()
            return Response(data)
    View Code

    在api目录下创建md文件夹,在里面创建cors.py

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    
    class CorsMiddleware(MiddlewareMixin):
    
        def process_response(self,request,response):
            # 设置响应头
            response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
           
            return response
    View Code

    修改settings.py,注册自定义中间件

    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        'api.md.cors.CorsMiddleware',
    ]
    View Code

    刷新vue页面,效果同上!

    模拟复杂请求

    修改HelloWorld.vue,增加登录按钮。发送固定的用户名和密码,指定数据格式为json

    <template>
      <div class="hello">
        <h1>{{ msg }}</h1>
        <button v-on:click="doLogin">登录</button>
        <h1>课程列表</h1>
        <ul v-for="item in courseList">
          <li>{{item.name}}</li>
        </ul>
      </div>
    </template>
    
    <script>
    export default {
      name: 'HelloWorld',
      data () {
        return {
          msg: '欢迎使用路飞学城',
          courseList:[],
        }
      },
      mounted(){  //页面加载完成后
        this.initCourse();  //执行此方法
      },
      methods:{
        initCourse:function () {
          let that = this
          //向后台发送ajax请求
          this.$axios.request({
            url:'http://127.0.0.1:8000/api/v1/courses/',
            method:'GET',
            responseType:'json',
          }).then(function (arg) {
            //成功之后
            console.log(arg);
            if (arg.data.code == 1000){
              //更新数据
              that.courseList = arg.data.data;
            }else {
              //弹出错误
              alert(arg.data.error);
            }
    
          }).catch(function (err) {
            //发生错误
            console.log(err);
          })
        },
        doLogin(){
          this.$axios.request({
            url:'http://127.0.0.1:8000/api/v1/auth/',
            method:'POST',
            data:{
              //用户名和密码
              user:'xiao',
              pwd:'123',
            },
            //增加headers头
            headers:{
              //指定数据格式
              'Content-Type':'application/json',
            },
            //响应格式为json
            responseType:'json',
          }).then(function (arg) {
            //成功之后
            console.log(arg);
          }).catch(function (err) {
            //发生错误
            console.log(err);
          })
        }
      },
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
    h1, h2 {
      font-weight: normal;
    }
    ul {
      list-style-type: none;
      padding: 0;
    }
    li {
      display: inline-block;
      margin: 0 10px;
    }
    a {
      color: #42b983;
    }
    </style>
    View Code

    修改api_urls.py,增加路径

    from django.conf.urls import url
    from api.views import course,auth
    
    urlpatterns = [
        url(r'auth/$', auth.AuthView.as_view({'post': 'login'})),
        url(r'courses/$', course.CoursesView.as_view({'get': 'list', 'post': 'create'})),
        url(r'courses/(?P<pk>d+)/$', course.CoursesView.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'}))
    ]
    View Code

    在views目录下,创建文件auth.py

    from rest_framework.views import APIView
    from rest_framework.viewsets import ViewSetMixin
    from rest_framework.response import Response
    
    from api import models
    from api.serializers.course import CourseModelSerializer
    
    from api.utils.response import BaseResponse
    from api.utils.serialization_general import SerializedData
    
    
    class AuthView(ViewSetMixin,APIView):
        def login(self, request, *args, **kwargs):
            print('用户发了POST请求了',request)
            return Response({'code':'ok'})
    View Code

    刷新vue页面,点击登录

    查看Console,提示对方不允许

    查看Network,它是一个OPTIONS请求

    查看Pycharm控制台输出:

    [18/Aug/2018 21:37:04] "OPTIONS /api/v1/auth/ HTTP/1.1" 200 163

    通过以上信息,可以看出。浏览器发送OPTIONS请求,也就是在预检。但是后端没有同意,所以浏览器没有发送POST请求。

    怎么才能让它通过预检呢?

    修改md目录下的cors,做一个if判断,允许一下!

    from django.utils.deprecation import MiddlewareMixin
    from django.conf import settings
    
    class CorsMiddleware(MiddlewareMixin):
    
        def process_response(self,request,response):
            # 设置响应头
            response['Access-Control-Allow-Origin'] = 'http://localhost:8080'
            # 当为OPTIONS时
            if request.method == "OPTIONS":
                # 允许通过的请求方式
                response["Access-Control-Allow-Methods"] = "POST,PUT,DELETE"
                # 允许通过的请求头
                response["Access-Control-Allow-Headers"] = "Content-Type"
                
            return response
    View Code

    刷新页面,再次点击登录

    查看Console,发现请求正常

    查看Network,发现有2次请求

    查看第一个请求,它是OPTIONS请求方式

    查看第二个请求,它是POST请求方式

    查看Pycharm控制台输出:

    [18/Aug/2018 21:43:24] "GET /api/v1/courses/ HTTP/1.1" 200 394
    [18/Aug/2018 21:43:32] "OPTIONS /api/v1/auth/ HTTP/1.1" 200 163
    用户发了POST请求了 <rest_framework.request.Request object at 0x0000024630CD85C0>
    [18/Aug/2018 21:43:32] "POST /api/v1/auth/ HTTP/1.1" 200 13

    通过以上信息,可以看到。数据接收正常!

    通过这样,无论是简单,还是复杂,都允许。

    django cors组件

    CORS,全称为跨域资源共享。它允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。

    上面的cors.py,帮我们实现了cors。这个手写的,但是官方提供了cors组件,专门用来解决跨域问题的!

    1.安装django-cors-headers

    pip install django-cors-headers

    2.配置settings.py文件

    INSTALLED_APPS = [
        ...
        'corsheaders',  # 注册应用cors
     ] 
    
    MIDDLEWARE_CLASSES = (
        ...
        'corsheaders.middleware.CorsMiddleware', # 注册组件cors
    
    )
    #跨域增加忽略
    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    CORS_ORIGIN_WHITELIST = (
        '*'
    )
    
    CORS_ALLOW_METHODS = (
        'DELETE',
        'GET',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
        'VIEW',
    )
    
    CORS_ALLOW_HEADERS = (
        'XMLHttpRequest',
        'X_FILENAME',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
        'Pragma',
    )
    View Code

    注释掉自定义组件,settings.py完整代码如下:

    """
    Django settings for s11luffycity project.
    
    Generated by 'django-admin startproject' using Django 1.11.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/1.11/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/1.11/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = '-i@r%a=tf*0n6!kzd=m#gx9g82i7@!x=n9jx=jta&(7%zw67#!'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = []
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'api.apps.ApiConfig',
        'rest_framework',
        'corsheaders',  # 注册应用cors
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
        # 'api.md.cors.CorsMiddleware',
        'corsheaders.middleware.CorsMiddleware',  # 注册组件cors
    ]
    
    ROOT_URLCONF = 's11luffycity.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 's11luffycity.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/1.11/ref/settings/#databases
    
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/1.11/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    
    TIME_ZONE = 'UTC'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = True
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/1.11/howto/static-files/
    
    STATIC_URL = '/static/'
    REST_FRAMEWORK = {
        'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
        'VERSION_PARAM':'version',
        'DEFAULT_VERSION':'v1',
        'ALLOWED_VERSIONS':['v1','v2'],
        'PAGE_SIZE':20,
        'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    }
    
    #跨域增加忽略
    CORS_ALLOW_CREDENTIALS = True
    CORS_ORIGIN_ALLOW_ALL = True
    CORS_ORIGIN_WHITELIST = (
        '*'
    )
    
    CORS_ALLOW_METHODS = (
        'DELETE',
        'GET',
        'OPTIONS',
        'PATCH',
        'POST',
        'PUT',
        'VIEW',
    )
    
    CORS_ALLOW_HEADERS = (
        'XMLHttpRequest',
        'X_FILENAME',
        'accept-encoding',
        'authorization',
        'content-type',
        'dnt',
        'origin',
        'user-agent',
        'x-csrftoken',
        'x-requested-with',
        'Pragma',
    )
    View Code

    重启django项目,刷新页面,重启登录。效果同上!

    OK!问题解决!

    本文参考:

    https://blog.csdn.net/apple9005/article/details/54427902

  • 相关阅读:
    根据前序遍历和中序遍历重建二叉树
    Java内部类
    Java浅克隆和深度克隆
    【leetcode】1354. Construct Target Array With Multiple Sums
    【leetcode】1352. Product of the Last K Numbers
    【leetcode】1351. Count Negative Numbers in a Sorted Matrix
    【leetcode】1342. Number of Steps to Reduce a Number to Zero
    【leetcode】1343. Number of Sub-arrays of Size K and Average Greater than or Equal to Threshold
    【leetcode】1344. Angle Between Hands of a Clock
    【leetcode】1346. Check If N and Its Double Exist
  • 原文地址:https://www.cnblogs.com/xiao987334176/p/9457580.html
Copyright © 2011-2022 走看看