cors
内容回顾:
restful 规范
10个
除了 jsonp(它只可以发 get 请求), 还有cors(可以发任何请求)。
解决跨域一般方法:加响应头-
VUE + rest framework 示例
- 登录
- 课程列表
- 课程详细
-
django 里面的content-type:
- django 的组件,帮我们做跨表的操作。
今日内容
1. 跨域
- 浏览器的同源策略导致跨域。
浏览器的同源策略:浏览器会将请求拦截,对 ajax 请求进行阻拦。对 href,src属性都不阻拦。
ps:
小公司:静态文件服务器:js,cs 之类的静态都放在里面。
大公司:CDN
解决方案:
- 客户端浏览器
- 请求的是 pythondv
- 发送的请求是 $.ajax(pythonav)
-------------------------------------
- 政府站点 pythondv
-------------------------------------
- 我的站点 pythonav
这个解决方案:
第一种:修改 pythondv 网站的请求方式,改成 script
第二种:政府站点请求方式不用修改,在 pythonav 拿到数据返回的时候,加上响应头。
一般就是你返回响应头,客户端给你传 cookie。
2. 解决跨域请求的两种方式
- jsonp
- cors
3. 关于 jsonp
https://www.jianshu.com/p/9b01fccf66b7
4. 关于 cors 跨域
随着技术的发展,现在的浏览器可以主动支持设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,使得浏览器允许跨域请求。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
4.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
注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求
4.2 两者的区别
- 简单请求: 一次请求
- 非简单请求: 两次请求,在发送数据之前会先发一次请求做'预检',只有'预检'通过后,才会再次发送一次请求用于数据传输。
4.3 关于预检
- 请求方式:OPTIONS *****
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
> 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
> 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
4.4 关于 cors 的优缺点
- 优点: 可以发送任意请求,而 jsonp 只可以发送 get 请求。
- 缺点: 当前请求如果是复杂请求的时候得先做个预检,再发真实的请求。发送两次请求可能会有性能上的损耗。
5. jsonp 和 cors 的区别。
JSONP:服务端不用修改,需要改前端。发jsonp请求
JSONP:只能发GET请求
CORS:前端的代码不用修改,服务端的代码需要修改。如果是简单请求的话在服务端加上一个响应头。
CORS:可以发任意请求
6. 基于 cors 实现 ajax 请求
6.1 支持跨域,简单请求。
服务器设置响应头: Access-Control-Allow-Origin = '域名' 或 '*'
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device- width">
<title>Title</title>
</head>
<div>
<h1>welcome</h1>
<button onclick="getData()">获取用户数据</button>
</div>
<script src="/static/jquery-1.12.4.min.js"></script>
<script>
function getData() {
$.ajax({
url:'http://127.0.0.1:8080/index/',
type:"GET",
success:function (data) {
console.log(data)
}
})
}
</script>
</body>
</html>
from django.shortcuts import render
from django.http import JsonResponse
from rest_framework.views import APIView
# Create your views here.
class IndexView(APIView):
def get(self,request,*args,**kwargs):
ret = {
'code': 100,
'data': '777'
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
6.2 支持跨域,复杂请求。
- 如果是复杂请求在你真正的发请求之前,会先偷偷的发一个OPTION请求,先预检一下,我允许你来你才来。如果要想预检通过,就需要写个 option 请求。
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
- “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
- “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
- “预检”缓存时间,服务器设置响应头:Access-Control-Max-Age
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width">
<title>Title</title>
</head>
<body>
<input type="button" value="获取用户数据" onclick="getUser()">
<script src="/static/jquery-1.12.4.min.js"></script>
<script>
function getUser() {
$.ajax({
url:'http://127.0.0.1:8080/user/',
type:'POST',
data:{'k1':'v1'},
headers:{
'h1':'777777'
},
success:function (ret) {
console.log(ret)
}
})
}
</script>
</body>
</html>
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from rest_framework.views import APIView
class UserIndex(APIView):
def get(self,request,*args,**kwargs):
ret = {
'code': 100,
'data': 'ppp'
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
def post(self,request,*args,**kwargs):
print(request.POST.get('k1'))
ret = {
'code':101,
'data':'fffff',
}
response = JsonResponse(ret)
response['Access-Control-Allow-Origin'] = "*"
return response
def options(self, request, *args, **kwargs):
# self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
# self.set_header('Access-Control-Allow-Headers', "k1,k2")
# self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
# self.set_header('Access-Control-Max-Age', 10)
response = HttpResponse()
response['Access-Control-Allow-Origin'] = '*'
response['Access-Control-Allow-Headers'] = 'h1'
# response['Access-Control-Allow-Methods'] = 'PUT'
return response
6.3 跨域获取响应头
- 默认获取到的所有响应头只有基本信息,如果想要获取自定义的响应头,则需要再服务器端设置Access-Control-Expose-Headers。
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>
<input type="submit" onclick="XmlSendRequest();" />
</p>
<p>
<input type="submit" onclick="JqSendRequest();" />
</p>
<script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
// 获取响应头
console.log(xhr.getAllResponseHeaders());
}
};
xhr.open('PUT', "http://c2.com:8000/test/", true);
xhr.setRequestHeader('k1', 'v1');
xhr.send();
}
function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'PUT',
dataType: 'text',
headers: {'k1': 'v1'},
success: function(data, statusText, xmlHttpRequest){
console.log(data);
// 获取响应头
console.log(xmlHttpRequest.getAllResponseHeaders());
}
})
}
</script>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
def put(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('xxoo', "seven")
self.set_header('bili', "daobidao")
self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
self.write('{"status": true, "data": "seven"}')
def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Headers', "k1,k2")
self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
self.set_header('Access-Control-Max-Age', 10)
6.4 跨域获取 cookie
- 在跨域请求中,默认情况下,HTTP Authentication信息,Cookie头以及用户的SSL证书无论在预检请求中或是在实际请求都是不会被发送。
如果想要发送:
- 浏览器端:XMLHttpRequest的withCredentials为true
- 服务器端:Access-Control-Allow-Credentials为true
- 注意:服务器端响应的 Access-Control-Allow-Origin 不能是通配符 *
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>
<input type="submit" onclick="XmlSendRequest();" />
</p>
<p>
<input type="submit" onclick="JqSendRequest();" />
</p>
<script type="text/javascript" src="jquery-1.12.4.js"></script>
<script>
function XmlSendRequest(){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
var result = xhr.responseText;
console.log(result);
}
};
xhr.withCredentials = true;
xhr.open('PUT', "http://c2.com:8000/test/", true);
xhr.setRequestHeader('k1', 'v1');
xhr.send();
}
function JqSendRequest(){
$.ajax({
url: "http://c2.com:8000/test/",
type: 'PUT',
dataType: 'text',
headers: {'k1': 'v1'},
xhrFields:{withCredentials: true},
success: function(data, statusText, xmlHttpRequest){
console.log(data);
}
})
}
</script>
</body>
</html>
class MainHandler(tornado.web.RequestHandler):
def put(self):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Credentials', "true")
self.set_header('xxoo', "seven")
self.set_header('bili', "daobidao")
self.set_header('Access-Control-Expose-Headers', "xxoo,bili")
self.set_cookie('kkkkk', 'vvvvv');
self.write('{"status": true, "data": "seven"}')
def options(self, *args, **kwargs):
self.set_header('Access-Control-Allow-Origin', "http://www.xxx.com")
self.set_header('Access-Control-Allow-Headers', "k1,k2")
self.set_header('Access-Control-Allow-Methods', "PUT,DELETE")
self.set_header('Access-Control-Max-Age', 10)