跨域

浏览器的同源策略

浏览器有一个同源策略,它会阻止非同源的请求。非同源是指 域名不同 或者 端口不同。浏览同源策略只对 表单、ajax请求生效,不同拦截src的请求。

​ 例如: 我在 127.0.0.1:8000 的页面 去调用 127.0.0.1:8001/api/test , 就会被浏览器拦截,因为端口不同,属于非同源请求。

​ src请求是指这种标签中带有src属性的请求:

<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>

<img src="http:/127.0.0.1:8082/media/pic/5cm.jpg">

什么是 CORS 请求?

CROS (cross origin resource share): 跨域资源共享。

请求方 与 被请求资源 为 非同源的请求就是 CORS 请求,也叫做跨域请求,分为两种:

  • 简单请求
1. http 方法 为 HEAD / GET / POST 

2. http 请求头不超出这几种字段(可以没有但不能超出)

      Accept,  Accept-Language,  Content-Language,  Content-Type

      其中 Content-Type 只能是以下几种类型:

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

  • 复杂请求

    不满足简单请求条件的都属于复杂请求。

解决跨域问题

跨域问题重现

  1. 在 django 中开一个后端接口 http://127.0.0.1:8000/api/test
#新增路由
from app01.views import Cors_test
path('api/test', Cors_test.as_view(),),

# 新增视图函数
from rest_framework.views import APIView
from rest_framework.response import Response

class Cors_test(APIView):
    def get(self,request):
        return Response('ok')
  1. 在前端代码中使用 ajax 调用后端的接口

Document ```
  1. 以服务器的方式打开前端代码文件 http://127.0.0.1:5500/templates/test.html ,并触发ajax请求,跨域报错

JSONP

jsonp 就是将需要跨域的链接作为src请求获取后端资源,避免跨域拦截。这样的方式有一些局限性,只能发送get请求,现在一般不用了。例如:

html 代码

<!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.0">
    <title>Document</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" referrerpolicy="no-referrer"></script>
</head>
<body>
    <button id="btn1">click</button>
    <script type="text/javascript">
        // 定义一个handle函数,供后端调用传参
        function handle(data){
            // 这里写你需要的操作
            alert(data)
        }
    </script>

    <!-- 调用后端接口,后端返回的内容将作为js代码执行 -->
    <script type="text/javascript" src='http://127.0.0.1:8000/api/test'></script>
</body>
</html>

后端的视图函数

class Cors_test(View):
    def get(self,request):
        # 这里的handle('ok')传到前端将会看作js代码执行,即执行html的handle函数
        response = HttpResponse("handle('ok')")
        return response

访问测试:

vscode 安装 live Server插件,使我们的html文件可以以服务器的方式打开。

理想情况下现在应该是alert('ok'),跳出一个ok的弹窗。但是这里报了一个CORB的错误。

什么是CORB?

全称为 Cross-Origin Read Blocking,跨域读取阻止,浏览器在加载可以跨域资源时,在资源载入页面之前,对其进行识别和拦截的算法:

这个算法中有一个要点和本文相关:

  • 如果 response 包含 X-Content-Type-Options: nosniff 响应头部,下面两种情况的请求将被阻止:

    • 请求类型是"style” 但是 MIME 类型不是 “text/css”,
    • 请求类型是"script” 但是 MIME 类型不是JavaScript MIME 类型。

    请求类型我们可以看请求头中的Sec-Fetch-Dest, MIME 类型可以看返回头的 Content-Type, 请求的数据类型和返回的数据类型不一致,就很容易被 CORB机制拦截。

怎么解决此处CORB的拦截问题?

将 Content-Type 请求头 设置为 text/javascript;charset=UTF-8 或者 X-Content-Type-Options 请求头设置为空。

所以视图函数的中配置:

class Cors_test(APIView):
    def get(self,request):
        response = HttpResponse("handle('ok')")
        # response['X-Content-Type-Options'] = ""
        response['Content-Type']="text/javascript;charset=UTF-8"

        return response

看一下效果:

添加请求头

在 django 中,自定义中间件,给返回的response添加请求头。

对于简单请求,添加 Access-Control-Allow-Origin 请求头即可:

因为简单请求的请求方法以及请求头只要允许跨域,就默认允许携带。

from django.utils.deprecation import MiddlewareMixin

class Mymiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:5500'
        return response

对于复杂请求:

例如ajax中使用put方法,会报错:

复杂请求时,浏览器会先发一个预检请求 ,就是这个options,这里报错 put请求不被允许。

ajax中添加简单请求中没有的请求头 contentType: 'application/json' 时,也会报错

所以就需要在中间件中添加 allow headers 与 allow methods

class Mymiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        # 简单请求只需加这一个响应头
        response['Access-Control-Allow-Origin'] = 'http://127.0.0.1:5500'
        
        # 复杂请求还需额外添加对应的响应头
        if request.method == 'OPTIONS':
            response["Access-Control-Allow-Methods"] = "PUT,PATCH,DELETE"
            response["Access-Control-Allow-Headers"] = "Content-Type"
        return response

此时前端请求代码为:

<button id="btn1">click</button>
<script type="text/javascript">
    $('#btn1').click(
        function(){
            $('#send').attr('src','http://127.0.0.1:8000/api/test')
            $.ajax({
                // put方法 --复杂请求
                method: 'put',
                url: 'http://127.0.0.1:8000/api/test',
                // 'application/json'格式 --复杂请求
                contentType: 'application/json',
                success: function(data){
                    console.log(data)
                }
            })
        }
    )
</script>

后端视图函数代码为:

class Cors_test(APIView):
    def get(self,request):
        response = HttpResponse('ok')
        return response

    def put(self,request):
        response = HttpResponse('put ok')
        return response

访问效果:

posted @ 2022-05-25 09:52  huandada  阅读(130)  评论(0编辑  收藏  举报