Django跨域问题解决
今天在学习前端 Vue 框架的过程中,遇到了 跨域 相关问题
问题1详情:
Access to XMLHttpRequest at 'http://127.0.0.1:8000/book/' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
一、为什么会出现跨域
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
二、什么是跨域
当前页面URL | 被请求的URL | 是否跨域 | 原因 |
---|---|---|---|
http://www.baidu.com | http://www.baidu.com/index.html | 否 | 同源(协议、域名、端口号相同) |
http://www.baidu.com | https://www.baidu.com/index.html | 跨域 | 协议不同(http/https) |
http://www.baidu.com | http://www.taobao.com/ | 跨域 | 主域名不同(baidu/taobao) |
http://www.baidu.com | http://ditu.baidu.com/ | 跨域 | 子域名不同(www/ditu) |
http://www.baidu.com | http://www.baidu.com:8888/ | 跨域 | 端口号不同(80/8888) |
三、解决跨域
3.1 设置响应头
在视图函数中,在响应头中添加 response['Access-Control-Allow-Origin'] = '*'
,允许所有请求源。
...
class BookView(ModelViewSet):
...
def list(self, request, *args, **kwargs):
response = super().list(request, *args, **kwargs)
response['Access-Control-Allow-Origin'] = '*' # 设置响应头
return response
3.2 自定义中间件
由于每个响应头都需要设置,我们可以直接将功能放入自定义中间件。
自定义中间件 mymiddleware.py
from django.utils.deprecation import MiddlewareMixin
class CrossDomainMiddle(MiddlewareMixin):
def process_response(self, request, response):
response['Access-Control-Allow-Origin'] = '*' # 设置响应头
return response
注册中间件 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',
'app01.mymiddleware.CrossDomainMiddle' # 注册自定义的中间件
]
3.3 存在问题
问题2详情:
明明我们已经配置过跨域,而且 GET 请求已经成功,但是当我发送一个 DELETE 请求时,却依然报错。
Access to XMLHttpRequest at 'http://127.0.0.1:8000/book/5/' from origin 'http://localhost:63342' has been blocked by CORS policy: Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
同时我们发送的明明是 DELETE 请求, 而我们后端接收到的却是一个 OPTIONS 请求。
- 问题原因
当我们用到跨域资源共享(简称cors)来解决跨域问题,但是在使用cors的时候,http请求会被划分为两类,简单请求和复杂请求,而这两种请求的区别主要在于是否会触发cors预检请求。
简单来说,像 GET、POST、HEAD 这些规定为简单请求,而其他像 PUT、DELETE 等(非简单请求即为复杂请求)被规定为复杂请求,复杂请求会发送一个 OPTIONS 预检请求。当预检请求通过时,才会发起真正的请求,所以通常复杂请求通常需要两次请求才能得到结果。
- 解决方法
明确的设置响应头 Access-Control-Allow-Methods
中的跨域允许的请求方法。这个字段在简单请求的的时候比如 GET/POST 用 * 或者 不写 都可以,但是当使用 PUT/DELETE 等请求的时候,必须明确规定写出来
我们可以在 视图函数
中设置响应头或者通过 自定义中间件
(推荐) 统一设置
response['Access-Control-Allow-Origin'] = '*' # 设置 跨域允许的 请求源
response['Access-Control-Allow-Methods'] = 'GET,POST,OPTIONS,PUT,DELETE' # 设置 跨域允许的 请求方法
3.4 使用 django-cors-headers 模块
当然,想跨域这样的问题,早就有第三方模块帮我们解决好了,而且更为完美。我们可以不用手动处理,只需要使用别人的模块即可。
- 安装模块
pip install django-cors-headers
- 在 settings.py 中注册应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', # 你自己的应用
'rest_framework',
'corsheaders' # 注册 corsheaders
]
- 在 settings.py 中注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware', # 必须放在这个位置 CommonMiddleware 的上方
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', # 可以注释此中间件取消 csrf 认证
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
- 其他部分配置
在 settings.py 下方添加即可
############# 配置允许访问的域名白名单 #############
# 允许所有 域名/IP 跨域
CORS_ALLOW_ALL_ORIGINS = True
# 配置可跨域访问的 域名/IP
CORS_ALLOWED_ORIGINS = [
'127.0.0.1:8000',
'localhost:8080',
'myname.com',
]
# 使用正则表达式匹配允许访问的 域名/IP
CORS_ALLOWED_ORIGIN_REGEXES = [
r"^https://\w+.example.com$",
]
############# 配置允许的跨域请求方式 #############
# 配置允许的请求方式
CORS_ALLOW_METHODS = [
'*', # * 表示允许全部请求头
'GET',
'POST',
'PUT',
'PATCH',
'DELETE',
'OPTIONS'
]
############# 配置允许的请求头 #############
CORS_ALLOW_HEADERS = [
"accept",
"accept-encoding",
"authorization",
"content-type",
"dnt",
"origin",
"user-agent",
"x-csrftoken",
"x-requested-with",
]
############# 允许跨域访问Cookie #############
# 改为True即为可跨域设置Cookie
CORS_ALLOW_CREDENTIALS = True
# 这里有一个需要注意的点
# chrome升级到80版本之后,cookie的SameSite属性默认值由None变为Lax
# 也就是说允许同站点跨域 不同站点需要修改配置为 None(需要将Secure设置为True)
# 需要前端与后端部署在统一服务器下才可进行跨域cookie设置
# 总结:需要设置 samesite = none、secure = True(代表安全环境 需要 localhost 或 HTTPS)才可跨站点设置cookie
以上便是我遇见的两个与跨域相关的问题。
参考博客 https://blog.csdn.net/weixin_43939159/article/details/109676307
参考博客 https://juejin.cn/post/7171036423674396685