处理跨域请求
Posted 名刀
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了处理跨域请求相关的知识,希望对你有一定的参考价值。
浏览器具有同源策略,会禁止向与当前页面不同的域发送请求,只要是协议,域名,端口中有任何一个不同,都被当作是不同的域,这虽然是一种保护数据的机制,但是对我们开发来说确是个麻烦,解决办法有很多,这里介绍一下JSONP和CORS
先来理解一下浏览器这个同源策略是什么,就是浏览器禁止掉向其他域名发送的请求,其实是在请求回来的时候被禁掉的
哪些操作会受同源策略,哪些不会呢?
requests模块不受影响,因为没有经过浏览器
ajax发请求时,浏览器会限制(我们就是要解决这个问题)
有src属性的都不受同源策略限制 -img,script,iframe
但是注意,script中的src,拿到的数据会当作js代码执行,所以不能直接把数据放到src中
JSONP
jsonp是一种机智的方式,本质就是在远程发送数据的时候,在数据外层套一个函数名,把数据以函数的形式返回(这样才能被script识别)
在本地先定义一个函数,当远程发送过来数据,本地就当成一个函数执行,真实数据就相当于参数
jsonp本质就是创建一个script标签,把url放到src属性中,就是相当于发送了一个get请求,事实上jsonp只能发送get请求
用一个简单的示例来理解一下jsonp的本质
客户端的地址是:localhost:8000,要向服务端 localhost:8888 发送请求,获取数据,
利用jsonp来避开同源策略:
客户端:
<!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, initial-scale=1"> <title>Title</title> </head> <body> <button onclick="jsonp(\'http://127.0.0.1:8888/get_data?callback=func\')">获取数据</button>{#通过参数向服务端发送函数名,这样就能实现动态生成函数名#}
<script> function func(arg) { console.log(arg); document.head.removeChild(tag); } function jsonp(url) { tag = document.createElement(\'script\');//创建一个script标签,用来发送请求,注意是一个全局的变量,以便在func函数中删除 tag.src = url; document.head.appendChild(tag); //在html的头文件中添加这个script标签 } </script> </body> </html>
服务端:
from django.shortcuts import HttpResponse
def get_data(request):
func_name = request.GET.get(\'callback\')#从请求头中获取函数名
return HttpResponse(\'%s(数据)\'%func_name)#返回一个函数,把数据当作参数
1.jsonp就是利用script的src发送请求不会被受同源策略限制,然后通过src属性向服务端发送请求
2.script会把请求来的数据当成是js代码,所以服务端返回的数据不能直接是数据,会报错
3.把返回值封装成一个函数的形式,真实数据放在函数的参数中,客户端等收到后,从函数中拿到数据就行了
4.这个函数名最好是动态生成的,不然每次发送一个请求就要前后端商量好函数名,太麻烦
以上就是jsonp的原理
所以使用jsonp,本地需要定义一个函数,而远程需要把数据封装成一个函数
再来看看ajax如何实现跨域请求
<!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, initial-scale=1"> <title>Title</title> </head> <body> <button onclick="jsonp()">获取数据</button> <script> function func(arg) { console.log(arg); document.head.removeChild(tag); } {# 这种实际上就是内部帮我们实现创建一个script标签#} function jsonp() { $.ajax({ url:\'http://127.0.0.1/get_data\', type:\'GET\', dataType:\'JSONP\', jsonp:\'callback\', jsonCallback:\'func\'{# 这两行就是自动在url后面添加?callback=func #} }) } </script>
jsonp只能发送get请求,但是可不是所有的数据都能封装在参数中,想要发送post请求,还得用CORS
CORS跨站资源共享(Cross-Origin Resource Sharing)
再来看一下跨域请求时,浏览器的提示信息:
之所以ajax发送请求,返回的数据拿不到,是因为少了一点东西。提示缺少一个响应头,所以CORS的原理就是添加上这个响应头
注意一定要理解这个‘响应头’,响应头是通过响应对象response来设置的
只需要修改视图函数
from django.shortcuts import HttpResponse def get_data(request): response = HttpResponse(\'数据\') response[\'Access-Control-Allow-Origin\'] = \'http://127.0.0.1:8888\'#这样表示允许这个地址访问 #response[\'Access-Control-Allow-Origin\'] = \'*\'#这样表示允许所有地址访问 return response
这样就可以了,这是最简单的情况,本地不用做任何事,远程设置一个响应头
还有一种复杂点的情况:非简单请求
简单请求&非简单请求:
简单请求的条件:
1.请求方式为 HEAD,GET,POST
2.请求头中Content-Type的值是下面这三个中的一个
application/x-www-form-urlencoded
multipart/form-data
text/plain
同时满足以上两个条件时,才是简单请求,有一个不满足就是复杂请求
对于复杂请求,以PUT请求为例,会先发一个options请求,用来预检,对于预检请求,也要返回响应头
远程视图函数要这样处理:
def data(request): if request.method == \'OPTIONS\': #预检 response = HttpResponse() response[\'Access-Control-Allow-Origin\'] = \'*\' response[\'Access-Control-Allow-Methods\'] = \'PUT\'#允许这个请求头 return response elif request.method == \'PUT\': #预检通过后真正的请求 response = HttpResponse(\'数据\') response[\'Access-Control-Allow-Origin\'] = \'*\' return response
如果GET请求自定义请求头,也变成了复杂请求,也需要设置一下,允许这个请求头
比如自定义了一个请求头:xxx:yyyy
那远程代码中就要允许这个请求头
def data(request): if request.method == \'OPTIONS\': #预检 response = HttpResponse() response[\'Access-Control-Allow-Origin\'] = \'*\' #设置为星号表示同意任何跨域请求,还可以写某一个地址(协议+域名+端口) # response[\'Access-Control-Allow-Methods\'] = \'PUT,POST,DELETE\' response[\'Access-Control-Allow-Headers\'] = \'Content-Type,application/json\'#允许这个请求头
return response
elif request.method == \'PUT\': #预检通过后真正的请求
response = HttpResponse(\'数据\')
response[\'Access-Control-Allow-Origin\'] = \'*\'
return response
true
,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true
,如果服务器不要浏览器发送Cookie,删除该字段即可。XMLHttpRequest
对象的getResponseHeader()
方法只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。
Access-Control-Allow-Credentials :true
withCredentials
属性。var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理
但是,如果省略withCredentials
设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials
xhr.withCredentials = false;
Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。总结,解决跨域请求,常用的有三种方式:requests模块,cors,jsonp
以上是关于处理跨域请求的主要内容,如果未能解决你的问题,请参考以下文章