好比一个连接:http://www.baidu.com(端口默认是80端口),html
若是再来一个连接是这样:http://api.baidu.com,这个就算是跨域了(由于域名不一样)前端
再来一个:https://www.baidu.com,这个也是跨域了(由于协议不一样,用的https)vue
再来一个http://www.baidu.com:8888,这个也算跨域,端口号不一样python
举个实际的例子:ios
以上过程就发生了跨域访问。若是直接使用Ajax来请求就会失败,就像Chrome提示的:git
No 'Access-Control-Allow-Origin' header is present on the requested resource.github
跨域直白点就是,请求协议,域名(IP),端口号,三个其中任何一个不一样都算跨域npm
CORS(跨域资源共享,Cross-Origin Resource Sharing)是一种跨域访问的机制,可让Ajax实现跨域访问 django
那么若是跨域是什么样的呢?好比,我这里有个restframework项目做为服务器,项目名为drfversion,测试的app名叫testapp,url以下:json
view:
启动项目,直接访问这个API接口测试:
在django的templates目录下新建一个test.html,简单的创建了一个vue和axios的项目,做为前端的异步请求,写入如下参数:
点那个谷歌浏览器图标,而后直接谷歌浏览器打开,这里这个功能是pycharm给咱们提供的功能,此时这个做为客户端,访问:
里面的这个参数提示就是跨域了,浏览器默认有个同源策略,他检测到这个http://localhost:63342的url与咱们后端启动的DRF项目url:http://127.0.0.1:8000/test/不一样源,也就是跨域了,因此报错。
再看接口,看其实后端没有错误,已经给咱们返回了数据:
因此跨域请求的根本缘由是浏览器的同源策略形成的,要解决这个跨域请求就能够在这方面研究了
而跨域分简单请求和复杂请求
HTTP方法是只能是HEAD, GET和POST
HTTP头信息不超出如下几种字段:
并且Content-Type只能是下列类型:
除了简单请求的都是复杂请求了,因此若是传输json数据,Content-Type类型就是json了,因此必定是复杂请求了
复杂请求会先发出一个预请求,又叫预检,OPTIONS请求
由于看代码相信你也看到了,那个script脚本标签导入的两个js文件,一个用的vue,一个用的axios,而后用到的是各自的cdn,url也都不同:
其实也算跨域了,可是浏览器并无给咱们拦截,因此咱们是否也能够直接用script的src属性来访问,固然能够:
刷新刚才那个前端客户端页面,发现真的能够这样
那么再大胆一点,发一个函数名怎么样?而后前端先定义好这个函数名,试试看:
后端的视图函数里返回这个函数调用的字符串:
前端访问,可行,确实打印了
可是,这只能作GET啊,若是作POST,PUT啥的请求呢?就没办法了,并且在调用函数时,前端和后端必须商量好函数名,必须统一才行,因此这jsonp仍是有很大的弊端的
修改views.py中对应API的实现函数,容许其余域经过Ajax请求数据:
def myview(_request): response = HttpResponse(json.dumps({"key": "value", "key2": "value"})) response["Access-Control-Allow-Origin"] = "*" response["Access-Control-Allow-Methods"] = "POST, GET, OPTIONS" response["Access-Control-Max-Age"] = "1000" response["Access-Control-Allow-Headers"] = "*" return response
GET:
如今什么都不作,只是get请求,再看下提示的是什么:
注意这句话:No 'Access-Control-Allow-Origin' header is present on the requested resource.
好,在后端写一个中间件:
配置文件里应用上:
重启项目,前端再次访问,此次终于没有红色的报错了
POST:
再来个POST请求看看:
后端的view:
其余后端代码不变,前端访问,又变红了
注意这句话:Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
好的中间件作以下修改:
重启访问,有了:
OPTIONS(PUT,DELETE):
好若是是put或者delete请求呢?这个按前面说的简单请求与复杂请求的说法,这两个请求方式确定是复杂请求了,即那个OPTIONS了,搞一个put的请求看看,仍是先看看它报什么错:
view:
重启访问,报错提示的字段跟前面的POST请求字段好像是一个说法对吧
可是咱们只加了post的,并没加put或者delete的,修改中间件:
重启访问,确实已解决
而后,其实中间件仍是稍微优化一下,把简单请求和复杂请求作个判断处理,复杂请求能够预检嘛,节省下资源:
重启访问,没有任何问题:
相关代码:
中间件middle:
from django.utils.deprecation import MiddlewareMixin class CorsMiddle(MiddlewareMixin): def process_response(self, request, response): response['Access-Control-Allow-Origin'] = '*' if request.method == 'OPTIONS': response['Access-Control-Allow-Headers'] = 'Content-Type' response['Access-Control-Allow-Methods'] = 'PUT,DELETE' return response
view:
from rest_framework.views import APIView from rest_framework.views import Response class TestView(APIView): def get(self, request): return Response('跨域测试') def post(self, request): return Response('post接口测试') def put(self,request): return Response('put请求测试')
....(还有个delete请求就省略了,跟put请求同样的)
settings:
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', 'middle.CorsMiddle', ]
前端代码test.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>test</title> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> // function PrintTest() { // console.log('jsonp test'); // } </script> <script src="http://127.0.0.1:8000/test/"></script> </head> <body> <div id="app"></div> <script> const app = new Vue({ el: '#app', mounted() { axios.request({ url: 'http://127.0.0.1:8000/test/', method: 'GET',// "POST","PUT","DELETE" //data:{ // "name":"jack", //} }).then(function (data) { console.log(data) }) } }) </script> </body> </html>
以上4个方法,已经能够解决大部分的跨域请求问题,可是因为我后期再次遇到过更强大的问题:以上4个方法针对火狐浏览器无法,火狐浏览器安全性比较高,同样会报跨域请求,即便你在django的中间件添加了response处理
下面这个使用第三方库django-cors-headers的方法就能够完美解决火狐浏览器问题
在Django中,有人开发了CORS-header的middleware,github:传送门
只需在settings.py中作一些简单的配置便可,其余不用做任何修改,咱们也不用本身手动的建立中间件对response处理了,直接用如下配置便可, 如今用起来服务器端彻底开放,开启CORS,没有任何跨域烦恼
pip install django-cors-headers
INSTALLED_APPS = ( ... 'corsheaders', ... ) ... MIDDLEWARE_CLASSES = ( ... 'corsheaders.middleware.CorsMiddleware', # 注意顺序,必须放在Comon前面 'django.middleware.common.CommonMiddleware', ... )
#如下直接赋值放在settings.py里便可解决 CORS_ALLOW_CREDENTIALS = True CORS_ORIGIN_ALLOW_ALL = True CORS_ORIGIN_WHITELIST = ( '*' ) 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', )
- 针对跨域请求,其实本质上是浏览器对返回的结果response的拦截
- 最多见的解决办法就是在后端返回结果response时作数据处理,让浏览器不拦截
- 大杀招使用django-cors-headers库,秒杀一切,简直6得不行