一,为什么会有跨域问题
跨域问题的出现,是因为浏览器的同源策略对ajax请求进行阻拦了,但是并不是所有的请求都给做当做跨域,;像是一般的href属性,a标签什么的都不进行拦截
二,什么是同源策略
同源策略是一种约定,它是浏览器最核心也会是最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
它约定请求的url地址,必须与浏览器的url地址处于同域上,也就是域名,端口,协议都相同。
如果不同,就会报错:
已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8001/SendAjax/ 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。
实际上的结果是,请求以及被发送过去了,目标服务器也对做出了响应,只是浏览器对非同源请求的放回结果做了拦截。
三,CORS简介
CORS,跨域资源共享,它需要浏览器和服务器同事支持,目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信中,都是浏览器自动完成的,不需要用户的参与,对于开发者来说cors通信与同源的ajax没有差别,代码完全一样。浏览器一旦发现ajax请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有察觉。
要实现cors通信,主要是在服务器,也就是获得数据的一方做处理。
四,cors基本的流程
1,浏览器将请求分为两类,一类是简单请求,一类是非简单请求,满足下列条件的就是简单请求,否则即使非简单请求:
条件: 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 注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
2,简单请求和复杂请求的区别:
简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发第一次请求做预检,只有预检通过后在发一次请求作为数据传输。
3,关于预检:
- 请求方式:OPTIONS - “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息 - 如何“预检” => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过 Access-Control-Request-Method => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过 Access-Control-Request-Headers
4,CORS优缺点
CORS的优点:可以发任意请求
CORS的缺点:上是复杂请求的时候得先做一个预检,再发真实的请求,发了两次请求会有性能上的损耗。
五,实现CORS请求
局部使用:
简单请求(get)客户端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <button id="a" class="btn btn-success">go</button> <script> $("#a").click(function () { $.ajax({ url:'http://127.0.0.1:8000/time/', type:'get', success:function (data) { alert(data) } } ) }) </script> </body> </html>
简单请求服务端:
def get_time(request): if request.method == 'GET': ntime = time.strftime('%Y-%m-%d %X') obj = HttpResponse(ntime) obj['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001' return obj
复杂请求(post+contentType)客户端:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script> <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script> </head> <body> <button id="a" class="btn btn-success">go</button> <script> $("#a").click(function () { $.ajax({ url:'http://127.0.0.1:8000/money/', type:'post', contentType:'application/json', data:{"name":"yjh"}, success:function (data) { alert(data) } } ) }) </script> </body> </html>
复杂请求服务端:
from django.http import QueryDict def get_money(request): # print(request.body) if not request.body: obj = HttpResponse('1000000000000') else: data = request.body # post请求传过来的数据以二进制的形式存放于request.body # 将request.body的二进制数据转化为字典形式 dic = QueryDict(data.decode('utf-8'),encoding='utf-8') name = dic.get('name') obj = HttpResponse(name) if request.method == 'OPTIONS': obj['Access-Control-Allow-Headers'] = 'Content-Type' obj['Access-Control-Allow-Origin'] = 'http://127.0.0.1:8001' return obj
六:全局配置
由于是全局配置,所以应该写在中间件中。然而,我们知道浏览器是在返回的时候出现的禁用,因此我们我们需要重写process_response方法。
第一步:在应用文件夹根目录中,新建一个my_middleware.py文件
第二步:在该py文件夹中写上:
from django.utils.deprecation import MiddlewareMixin class CorsMiddleWare(MiddlewareMixin): def process_response(self,request,response): if request.method=="OPTIONS": #可以加* response["Access-Control-Allow-Headers"]="Content-Type" response["Access-Control-Allow-Origin"] = "http://localhost:8080" return response
第三步:在settings.py中配置:
MIDDLEWARE = [ 'app01.my_middleware.CorsMiddleware', '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', ]