DC's blog DC's blog
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)

DC

愿我一生欢喜,不为世俗所及.
首页
  • 计算机基础
  • linux基础
  • mysql
  • git
  • 数据结构与算法
  • axure
  • english
  • docker
  • opp
  • oop
  • 网络并发编程
  • 不基础的py基础
  • 设计模式
  • html
  • css
  • javascript
  • jquery
  • UI
  • 第一次学vue
  • 第二次学vue
  • Django
  • drf
  • drf_re
  • 温故知新
  • flask
  • 前后端不分离

    • BBS
    • 订单系统
    • CRM
  • 前后端部分分离

    • pear-admin-flask
    • pear-admin-django
  • 前后端分离

    • 供应链系统
  • 理论基础
  • py数据分析包
  • 机器学习
  • 深度学习
  • 华中科大的网课
  • cursor
  • deepseek
  • 杂文
  • 罗老师语录
  • 关于我

    • me
  • 分类
  • 归档
GitHub (opens new window)
  • Django

  • 第一次学drf

  • 第二次学drf

    • drfCBV
    • 认证
    • 权限
    • 限流
    • 版本
    • 解析器
    • 元类
    • 序列化使用
    • 序列化源码
    • 验证&源码
    • 序列化+验证
    • Serializer案例
    • Serializer总结
    • 分页
    • 视图+路由+过滤
    • 练习+跨域问题
      • 需求
      • 创建四张表
      • 登陆接口
        • app01/views.py
        • urls.py
        • Postman验证
      • 其它需求实现
        • app01/serializer.py
        • app01/auth.py
        • app01/permission.py
        • app01/serializer.py
        • app01/views.py
        • app01/urls.py
        • Postman验证
      • 跨域问题及解决!!
        • 同源策略
        • CORS
        • 简单/非简单请求
        • 中间件 - 解决跨域
        • django-cors-headers
    • 博客练习
    • 源码分析汇总
  • 温故知新

  • flask

  • 后端
  • 第二次学drf
DC
2023-11-20
目录

练习+跨域问题

以下的代码实现跟实战项目供应链中相似功能的写法有些许不一样, 但达到的效果差不多.. 不必纠结.

# 需求

练练手,完成一个练习..

需求如下:

1. 有车型(CarModel)、车厂(CarFactory)、经销商(Distributor)三张表.
   一个车厂可以生产多种车型、一个经销商可以出售多种车型、一个车型可以有多个经销商出售.
   车型 - 车型名, 车型出厂价, 车厂id
   车厂 - 车场名, 车厂地址, 联系电话
   经销商 - 经销商名, 地址, 联系电话
2. 有用户表,基于Django内置user表,扩展mobile字段
3. 编写登陆接口, jwt的认证方式返回token
   格式为: {status:100,msg:'登陆成功',token:'12faf73as'}
4. 管理员登陆后可以 新增,删除 车型、车厂、经销商
5. 普通用户登陆后,可以查看车型 (群查分页/有哪些车型,单查/某个车型的详情)
   查车型返回车型信息,车厂名字,经销商名字和电话

加分项:
  用户注册接口
  管理员有用户锁定、删除功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

准备工作: 用pycharm创建新的Django项目: drf_car


# 创建四张表

step1: 创建表模型
step2: 在settings.py配置文件中进行用户扩展表的配置
step3: 执行两条数据库表的迁移命令.

app01/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser


# -- 用户表
class UserInfo(AbstractUser):
    mobile = models.CharField(max_length=32, verbose_name='用户电话')
    # is_lock = models.BooleanField(default=True, verbose_name='是否锁定')


# -- 车型表
class CarModel(models.Model):
    name = models.CharField(max_length=32, verbose_name='车型名')
    # -- 出厂价 CharField、IntegerField、DecimalField 皆可
    price = models.CharField(max_length=32, verbose_name='出厂价')
    # -- 写在多的那方
    carFactory = models.ForeignKey(to='CarFactory', on_delete=models.CASCADE)
    # -- 虚拟字段 n:n该字段写在哪边都行
    distributor = models.ManyToManyField(to='Distributor')


# -- 经销商表
class Distributor(models.Model):
    name = models.CharField(max_length=32, verbose_name='经销商名')
    addr = models.CharField(max_length=32, verbose_name='地址')
    phone = models.CharField(max_length=32, verbose_name='电话')


# -- 车厂表
class CarFactory(models.Model):
    name = models.CharField(max_length=32, verbose_name='车厂名')
    addr = models.CharField(max_length=32, verbose_name='车厂地址')
    phone = models.CharField(max_length=32, verbose_name='车厂电话')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

settings.py

AUTH_USER_MODEL = 'app01.UserInfo'
1

执行迁移命令

pycharm - Tools - Run manage.py Task..
(若更改了项目路径,就得自己手动写完整的命令啦!!)
"""命令:"""
   makemigrations
   migrate

使用的是默认的sqlite3,没有使用mysql
使用pycharm连接sqlite3数据库. 数据库 - 数据源 sqlite - 选择文件路径 - 先Test测试下连接是否成功 - 确认连接
(这里使用jdbc去连接的,何为jdbc?)
   -- jdbc是java中连接关系型数据库的一个驱动,java官方写了一些连接数据库的接口. 
      不同的厂商oracle、mysql、sqlite基于这些接口来写具体的实现.
      接口规范了子类的行为,方法是一样.. 方法里具体的实现不同.
   -- pycharm是用java写的.
连接好后,可以观察到,通过app01的model模型创建出了7张表,都默认以app01作为前缀.
   app01_carfactory
   app01_carmodel
   app01_carmodel_distributor
   app01_distributor
   app01_userinfo
   app01_userinfo_groups
   app01_userinfo_user_permissions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

准备工作: 通过createsuperuser命令创建两个超级用户!!
dc admin123 手动添加mobile手机号字段的值 123123123 超级用户
egon admin123 手动添加mobile手机号字段的值 234234234 并修改is_is_superuser字段的值为false. 变为普通人.


# 登陆接口

需求: 编写登陆接口, jwt的认证方式返回token

回顾: 第一次学drf/drf的路由组件.md 的内容

逻辑:前端传递<用户名密码or手机号密码>到后端. 若校验通过,签发Token.
若视图类就只继承APIView.里面写get、post等方法(自己写5个接口)
   那么路由必须写成 path('api/login/', views.UserView.as_view()),
   -- 使用postman对http://127.0.0.1:8000/api/login/ 发送post请求就会访问该路由. api/login/ 是我们自己商定的.
当然,若想自动生成路由.视图类必须得继承ViewSetMixin! 再加上APIView,即继承 ViewSet!! 
   (并不是说只有ViewSet这一种继承方式,可根据不同的需求,使用不同的视图组件进行不同组合.唯一确定的是必须包含ViewSetMixin)

这里我们采用自动生成路由的形式!还使用了action装饰器!!
1
2
3
4
5
6
7
8
# app01/views.py

下方是自定义用户表签发Token.
回顾: 第一次学drf/jwt.md/自定义用户表签发token 对应的内容
还有可以快速签发和认证,但快速的方式发送的json字符串的用户名必须是 username, 密码必须是 password.
回顾: 第一次学drf/jwt.md/快速签发和认证 对应的内容

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework_jwt.settings import api_settings
from .models import UserInfo

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request):
        # 1.取到前端传入的手机号和密码
        mobile = request.data.get('mobile')
        password = request.data.get('password')
        # 2.去数据库中校验,若有这个用户,则登陆成功,签发token
        # -- 这种方式不对,密码是明文的
        # from .models import UserInfo
        # UserInfo.objects.filter(mobile=mobile, password=password)
        # -- 有问题,authenticate只能使用用户名和密码
        # from django.contrib.auth import authenticate
        # user = authenticate(request, mobile=mobile, password=password)
        user = UserInfo.objects.filter(mobile=mobile).first()
        if user and user.check_password(password):
            # 用户正常登陆,签发Token
            payload = jwt_payload_handler(user)  # -- 通过user拿到payload
            token = jwt_encode_handler(payload)  # -- 通过payload拿到token
            return Response(data={'code': 100, 'mag': '登录成功', 'token': token})
        else:
            return Response(data={'code': 999, 'msg': '手机号或密码错误.'})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter

from app01 import views

router = SimpleRouter()
router.register('user', views.UserView, 'user')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
# Postman验证

运行Django项目时,报rest_framework_jwt模块的错误,我确信写的没问题... 卸载重新安装该模块就好了.. 我真的会谢!!


# 其它需求实现

需求:
管理员登陆后可以 <新增,删除> 车型、车厂、经销商
普通用户登陆后,可以查看车型 (群查分页/有哪些车型,单查/某个车型的详情)

○ 管理员的操作
step1: 快速编写 车型(CarModel)、车厂(CarFactory)、经销商(Distributor)这三张表的接口 每张表的5个接口!!共15个.
step2: 写好后, 根据需求 <新增,删除> , 对写好的代码进行需改. 每个视图类的5个接口变为2个接口..Hhh
            视图类继承 ModelViewSet --> 改为继承 GenericViewSet, CreateModelMixin, DestroyModelMixin
step3: 根据需求 <登陆后> <管理员> 需要添加 认证和权限 的功能.

○ 普通人的操作
step1: 再次快速编写 车型(CarModel) 的5个接口,根据需求             视图类继承 ModelViewSet --> 改为继承 GenericViewSet, ListModelMixin, RetrieveModelMixin
step2: 根据需求 <登陆后> 需要添加 认证和分页 的功能.

需求分析总结:
管理员操作对应的3个视图类所实现的接口 登陆 + 管理员 才能访问.
普通人操作对应的1个视图类所实现的接口 登陆 就可以访问,不管当前登陆的是管理员还是普通用户!!

# app01/serializer.py

在app01下新建serializer.py文件,写入以下内容,用于序列化

from .models import CarModel, CarFactory, Distributor
from rest_framework import serializers


class CarModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = CarModel
        fields = '__all__'


class CarFactorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CarFactory
        fields = '__all__'


class DistributorSerializer(serializers.ModelSerializer):
    class Meta:
        model = Distributor
        fields = '__all__'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# app01/auth.py

在app01下新建auth.py文件,写入以下内容,用于认证
回顾: drf/10_jwt.md/自定义用户表的认证类 对应的内容

from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions
from rest_framework_jwt.settings import api_settings
import jwt

from .models import UserInfo

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


# -- 自定义认证类JWTAuthentication继承BaseAuthentication,重写authenticate方法.
class JWTAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # -- 基本逻辑:认证通过,返回两个值;认证不通过,抛异常!
        jwt_value = request.META.get("HTTP_TOKEN")
        if not jwt_value:
            # -- 登陆接口通过jwt的认证方式返回token,前端再次访问服务器带着Token..
            raise exceptions.AuthenticationFailed("您没有携带Token,请携带Token后访问.")
        try:
            payload = jwt_decode_handler(jwt_value)
            # -- payload里有用户ID,再通过用户id拿到当前登陆用户!
            # 注:自己造出来的,后续取除了id,username这两字段以外,都可能有问题?? 嘛意思?
            # 可优化:user = UserInfo(id=payload['user_id'], username=payload['username'])
            user = UserInfo.objects.first(pk=payload['user_id']).first
        except jwt.ExpiredSignature:
            msg = '签名过期'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = '解码错误'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            msg = '不合法'
            raise exceptions.AuthenticationFailed(msg)
        # -- 返回的两个值,会给新的request.user,一旦认证通过,后续的request就能取出user!
        #    也就是在权限类里我能拿到这个user.
        return user, jwt_value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# app01/permission.py

在app01下新建permission.py文件,写入以下内容,用于实现权限功能
回顾: drf/7_认证权限频率.md/权限类编写 对应的内容

from rest_framework.permissions import BasePermission


class SuperUserPermission(BasePermission):
    def has_permission(self, request, view):
        if request.user.is_superuser:
            return True  # -- 超级用户允许访问
        else:
            # -- 不是超级用户,在前端显示的信息是英文的
            #    若想自定义显示的信息
            self.message = "您不是超级用户,不能查看!!"
            return False
1
2
3
4
5
6
7
8
9
10
11
12
# app01/serializer.py

在app01下新建page.py文件,写入以下内容,用于分页

from rest_framework.pagination import PageNumberPagination


# -- 基本分页类
class MyPageNumberPagination(PageNumberPagination):
    page_size = 2
    page_query_param = 'page'
    page_size_query_param = 'size'
    max_page_size = 5
1
2
3
4
5
6
7
8
9
# app01/views.py
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework.viewsets import ModelViewSet, GenericViewSet
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework_jwt.settings import api_settings

from .models import UserInfo, CarModel, CarFactory, Distributor
from .serializer import CarModelSerializer, CarFactorySerializer, DistributorSerializer
from .page import MyPageNumberPagination
from .auth import JWTAuthentication
from .permission import SuperUserPermission

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER

"""
登陆签发token
需求:编写登陆接口, jwt的认证方式返回token
"""
class UserView(ViewSet):
    @action(methods=['POST'], detail=False)
    def login(self, request):
        mobile = request.data.get('mobile')
        password = request.data.get('password')
        user = UserInfo.objects.filter(mobile=mobile).first()
        if user and user.check_password(password):
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response(data={'code': 100, 'mag': '登录成功', 'token': token})
        else:
            return Response(data={'code': 999, 'msg': '手机号或密码错误.'})


"""
管理员
需求:<管理员><登陆>后可以 新增,删除 车型、车厂、经销商
"""
# class CarModelView(ModelViewSet):
class CarModelView(GenericViewSet, CreateModelMixin, DestroyModelMixin):
    # -- 注:想让其有分页、过滤功能,定义类变量parser_classes、filter_backends
    queryset = CarModel.objects.all()
    serializer_class = CarModelSerializer
    # -- 认证、权限
    authentication_classes = [JWTAuthentication]  # -- 登陆后才能访问
    permission_classes = [SuperUserPermission]  # -- 超级用户才能访问


class CarFactoryView(GenericViewSet, CreateModelMixin, DestroyModelMixin):
    queryset = CarFactory.objects.all()
    serializer_class = CarFactorySerializer
    authentication_classes = [JWTAuthentication]
    permission_classes = [SuperUserPermission]


class DistributorView(GenericViewSet, CreateModelMixin, DestroyModelMixin):
    queryset = Distributor.objects.all()
    serializer_class = DistributorSerializer
    authentication_classes = [JWTAuthentication]
    permission_classes = [SuperUserPermission]


"""
普通人
需求:用户<登陆>后,可以查看车型 (群查分页/有哪些车型,单查/某个车型的详情)
"""
class CarModelSimpleView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
    queryset = CarModel.objects.all()
    serializer_class = CarModelSerializer
    # -- 认证、分页 不需要权限,只要登陆就能访问查看车型的接口,所以只设置了一个认证
    authentication_classes = [JWTAuthentication]
    pagination_class = MyPageNumberPagination
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# app01/urls.py
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import SimpleRouter

from app01 import views

router = SimpleRouter()
# -- 登陆接口
router.register('user', views.UserView, 'user')
# -- 管理员的接口 增/删 车型、车厂、经销商 -> 登陆 + 管理员 才能访问
router.register('car_model', views.CarModelView, 'car_model')
router.register('car_factory', views.CarFactoryView, 'car_factory')
router.register('distributor', views.DistributorView, 'distributor')
# -- 普通人的接口 查看车型(所有条分页/单条) -> 登陆 就能访问,不管是管理员还是普通用户
router.register('car_model_simple', views.CarModelSimpleView, 'CarModelSimpleView')


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(router.urls)),
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Postman验证

注:一般会将token过期时间设置为14天.. 默认的是300s.

# -- settings.py 里配置
import datetime

JWT_AUTH = {
    # -- 可以指定过期时间,比如1天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=1),
}
1
2
3
4
5
6
7

▲ 登陆接口

▲ 新增车厂

同理, 管理员新增 经销商是一样的套路, 我直接在数据库里添加经销商的数据.. Hhh

▲ 删除车厂

同理, 管理员删除 车型、车厂、经销商都是一样的套路.. 就不一一实验啦!!

▲ 新增车型

有了车厂和经销商后,才能新增车型,因为车型model里有n:1和n:n的关联..

▲ 查看车型

查看的车型中, 经销商的信息显示的是id号, 可以编写代码显示经销商具体信息.. 略.


多说两句 (⁎⁍̴̛ᴗ⁍̴̛⁎)

若想打通前后端..
就登陆功能而言, 登陆上后, 前端需要将token保存起来 - 难点: 如何axios发送post请求,并且在请求头里带token.
管理员有任务锁定,删除功能..
     前端列出一堆用户,一点击锁定,携带用户id和一个锁定标志0/1,将用户的is_lock字段置为True或False.
     相应的登陆接口要改一改,若锁定了,就不让其登陆!
还有个难点: 跨域问题.


# 跨域问题及解决!!

# 同源策略

浏览器的同源策略:
    它是浏览器的基本安全策略, 请求的url地址,必须与浏览器上的url地址处于同域上, 也就是域名,端口,协议相同.
也就是说不允许去不同的地址下拿数据!!
    eg: 我在本地上的域名是127.0.0.1:8000,请求另外一个域名:127.0.0.1:8001 上的一段数据, 浏览器就会报错.

目的: 防止恶意的网站窃取数据
    若浏览器对javascript没有同源策略的保护
    1) 那么做个仿淘宝前端 在页面中发送ajax请求 去淘宝服务器随便拿数据,都不需要后端啦.
    2) 不同源的数据和资源(如HTTP头、Cookie、DOM、localStorage等)就能相互随意访问,根本没有隐私和安全可言.

会出现跨域问题的场景: 前后端分离 、web端/PC端 (移动端、前后端不分离不会出现这个问题.)
过程!!!: 浏览器的请求出去啦 -> 后端代码也执行啦 (访问已经发生) -> 但后端返回的数据被浏览器的同源策略给拦截啦.

已拦截跨源请求:
同源策略禁止读取位于 http://127.0.0.1:8001/SendAjax/ 的远程资源.(原因:CORS 头缺少 'Access-Control-Allow-Origin').
1
2

强调: 虽出现跨域问题,但请求出去啦, 服务端也执行啦. 所以post请求修改数据, 数据会成功修改, 只不过返回的数据前端接收不到..

# CORS

cors跨域资源共享, 是后端的一个技术. - 用于解决同源策略导致不能跨域的问题!!
简单来说, 服务端在响应头中加入一些字段, 就可以跨域啦!!

# 简单/非简单请求

浏览器将CORS请求分成两类: 简单请求 (simple request) 和 非简单请求 (not-so-simple request)
只要<同时>满足以下两大条件, 就属于简单请求; 否则为非简单请求.

1. 请求方法是以下三种方法之一:
   HEAD
   GET
   POST
2. HTTP的头信息不超出以下几种字段:
   Accept
   Accept-Language
   Content-Language
   Last-Event-ID
   ★ Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
1
2
3
4
5
6
7
8
9
10

注: 也就是说Content-Type是json格式;或者get请求,请求头中带有一个Token. 这些请求都是非简单请求!!

浏览器对这两种请求的处理, 是不一样的!
简单请求只发送一次
非简单请求发送两次,第一次是OPTIPNS”预检“请求,只有“预检”通过后才再发送一次请求用于数据传输!

关于预检
- 请求方式:OPTIONS
- “预检”其实做检查,检查后端的跨域策略,如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     -> 若复杂请求是PUT等请求,则服务端需要设置 允许 <某请求> ,否则“预检”不通过
        Access-Control-Request-Method
     -> 如果复杂请求设置了请求头,则服务端需要设置允许 <某请求头> , 否则“预检”不通过
        Access-Control-Request-Headers

So,我们会遇到这样一个情况,json数据发送post请求,跨域不通过,查看时发现发送的是OPTIONS请求!!
1
2
3
4
5
6
7
8
9
10
# 中间件 - 解决跨域

简单请求解决跨域:
     <服务器>设置响应头: Access-Control-Allow-Origin = "域名" 或 "*"
一般来说, 为了安全起见, 不会写 * , 只会写前端地址..
但有些公司就写的*, 那就是意味着该公司允许你偷它的数据,Hhh. 非简单请求解决跨域:
     1> “预检”请求时, 允许<请求方式>则需<服务器>设置响应头: Access-Control-Allow-Method = "允许的请求方法" 或 "*"
     2> “预检”请求时, 允许<请求头>则需<服务器>设置响应头: Access-Control-Allow-Headers = "eg:token" 或 "*"
一般都设置为*

Ps: 一时间卡壳,域名是啥? 联想到DNS域名解析就顿悟了..

前端 HomeView.vue

<template>
    <div class="home">
        <h1>首页</h1>
        <button class="btn btn-success" @click="handleClick">点击</button>
    </div>
</template>

<script>
export default {
    name: 'HomeView',
    data() {
        return {}
    },
    methods: {
        handleClick() {
            // -- 简单请求
            // this.$axios.get(this.$settings.base_url + '/api/v1/home/banners/').then(res => {
            //     console.log(res.data)
            // })
            // -- 非简单请求,在请求头里随便加点东西,比如加个token.
            this.$axios.get(this.$settings.base_url + '/api/v1/home/banners/', {headers: {'token': '123wea1'}}).then(res => {
                console.log(res.data)
            })
        }
    },
    components: {}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

在 apps/home/文件夹下创建middle.py文件! 写入一下内容. 并在dev.py的中间件里注册!!
apps/home/models.py

# 随便导入一个dev.py中的中间件,查看其源码,仿写
# from django.contrib.sessions.middleware import SessionMiddleware
from django.utils.deprecation import MiddlewareMixin


# -- 自己处理跨域问题,跟web框架无关,所有的框架都可以这样处理.
class CorsMiddle(MiddlewareMixin):
    def process_request(self, request):
        # -- 看打印结果,验证简单请求发一次,非简单请求发两次.
        print(request.method)

    def process_response(self, request, response):
        # -- 在响应头中加入Access-Control-Allow-Origin,处理了简单请求! -- 任意网址
        response["Access-Control-Allow-Origin"] = "*"
        # -- 处理非简单请求
        if request.method == 'OPTIONS':
            response['Access-Control-Allow-Headers'] = '*'  # 允许所有的头 -- 任意请求头
            response['Access-Control-Allow-Method'] = '*'   # 允许所有的请求方式  -- 任意请求方式
        return response
      
"""记得注册中间件
MIDDLEWARE = [
    # ... ... ...
    'home.middle.CorsMiddle',
]
"""
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

简单请求验证

非简单请求验证

# django-cors-headers

django-cors-headers让我们写,自己也要会写.
写一个中间件,从配置文件中取,取了后进行映射.
比如:映射到response['Access-Control-Allow-Headers']等..

官方文档: https://github.com/adamchainz/django-cors-headers

step1: pip install django-cors-headers==3.8.0 不指定版本,安装最新的,会自动将django升到4.x. step2: 注册该第三方app.

INSTALLED_APPS = [
    # ... ...
    'corsheaders',
]
1
2
3
4

step3: 在中间件里进行配置.

MIDDLEWARE = [
    # ... ...
    'corsheaders.middleware.CorsMiddleware',
    'django.middleware.common.CommonMiddleware',
]
1
2
3
4
5

step4: 在dev.py中接着添加以下内容.

# ▲ 跨域问题
# CORS_ALLOW_CREDENTIALS = True
# CORS_ORIGIN_WHITELIST = (
#     '*'
# )

# CORS_ORIGIN_ALLOW_ALL = True  # -- 加上此句允许所有的跨域源, 该操作很危险
# -- 不设置CORS_ORIGIN_ALLOW_ALL = True,换成这一句也可以成功.. 主要是要看看官方文档.
CORS_ALLOWED_ORIGINS = ["http://localhost:8080",]

# -- 非简单请求的请求方式和请求头
# - 允许的请求方式
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',
    # -- 切记要设置请求头中额外允许添加的字段
    'token',
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

视图+路由+过滤
博客练习

← 视图+路由+过滤 博客练习→

最近更新
01
deepseek本地部署+知识库
02-17
02
实操-微信小程序
02-14
03
教学-cursor深度探讨
02-13
更多文章>
Theme by Vdoing | Copyright © 2023-2025 DC | One Piece
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式