您的位置:首页 > 房产 > 建筑 > 前后端分离django-restframework——解决跨域请求

前后端分离django-restframework——解决跨域请求

2024/10/6 18:20:36 来源:https://blog.csdn.net/xuezhe5212/article/details/140119595  浏览:    关键词:前后端分离django-restframework——解决跨域请求

为什么会出现跨域问题?
出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

什么是跨域
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

比如一个链接:http://www.baidu.com(端口默认是80端口),

如果再来一个链接是这样:http://api.baidu.com,这个就算是跨域了(因为域名不同)

再来一个:https://www.baidu.com,这个也是跨域了(因为协议不同,用的https)

再来一个http://www.baidu.com:8888,这个也算跨域,端口号不同

举个实际的例子:

  • API接口数据部署在baidu.com上;
  • Ajax文件部署在cnblogs.com上,Ajax文件会向API (baidu.com) 发送请求,返回数据;
  • 用户通过bing.com访问cnblogs.com的Ajax文件,请求数据

以上过程就发生了跨域访问。如果直接使用Ajax来请求就会失败,就像Chrome提示的:

No 'Access-Control-Allow-Origin' header is present on the requested resource.

跨域直白点就是,请求协议,域名(IP),端口号,三个其中任何一个不同都算跨域

CORS

CORS(跨域资源共享,Cross-Origin Resource Sharing)是一种跨域访问的机制,可以让Ajax实现跨域访问 

那么如果跨域是什么样的呢?比如,我这里有个restframework项目作为服务器,项目名为drfversion,测试的app名叫testapp,url如下:

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头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type

而且Content-Type只能是下列类型:

  • application/x-www-from-urlencoded
  • multipart/form-data
  • text/plain

复杂请求

除了简单请求的都是复杂请求了,所以如果传输json数据,Content-Type类型就是json了,所以一定是复杂请求了

复杂请求会先发出一个预请求,又叫预检,OPTIONS请求

常见的解决跨域的方法

1.协议和域名,端口都是用同一个就行
2.利用jsonp解决跨域问题

 因为看代码相信你也看到了,那个script脚本标签导入的两个js文件,一个用的vue,一个用的axios,然后用到的是各自的cdn,url也都不一样:

其实也算跨域了,但是浏览器并没有给我们拦截,所以我们是否也可以直接用script的src属性来访问,当然可以:

刷新刚才那个前端客户端页面,发现真的可以这样

那么再大胆一点,发一个函数名怎么样?然后前端先定义好这个函数名,试试看:

后端的视图函数里返回这个函数调用的字符串:

前端访问,可行,确实打印了

但是,这只能做GET啊,如果做POST,PUT啥的请求呢?就没办法了,而且在调用函数时,前端和后端必须商量好函数名,必须统一才行,所以这jsonp还是有很大的弊端的

3.修改Django中的views.py文件


修改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​

4.在后端做处理——Python是利用django中间件添加响应头 

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 MiddlewareMixinclass 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的方法就可以完美解决火狐浏览器问题

5.使用第三方库django-cors-headers

在Django中,有人开发了CORS-header的middleware

只需在settings.py中做一些简单的配置即可,其他不用作任何修改,我们也不用自己手动的创建中间件对response处理了,直接用以下配置即可,  现在用起来服务器端完全开放,开启CORS,没有任何跨域烦恼 


安装django-cors-headers:

pip install django-cors-headers

  

在settings.py中增加:
INSTALLED_APPS = (...'corsheaders',...)...MIDDLEWARE_CLASSES = (...'corsheaders.middleware.CorsMiddleware',  # 注意顺序,必须放在Comon前面'django.middleware.common.CommonMiddleware',...)#以下直接赋值放在settings.py里即可解决CORS_ALLOW_CREDENTIALS = TrueCORS_ORIGIN_ALLOW_ALL = TrueCORS_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的升级,如果你在安装的时候不指定版本的话,会默认安装最新版,且不指定django-cors-headers库版本,也会安装最新版,而以上内容是针对2.0的,所以,按照上面的配置的话,还是会报跨域请求错误,最根本的问题就是,一个协议的规则匹配

把上面的:

CORS_ORIGIN_WHITELIST = ( '*' )

换成以下的:

CORS_ORIGIN_WHITELIST = ('http://*.*.*:*', 'https://*.*.*:*')

其他不变,即可保证跨域请求正常,问题完美解决!!!

本次分享到此结束,感谢大家的阅读!

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com