zoukankan      html  css  js  c++  java
  • Sanic框架基础之解决CORS跨域

    在前后端分离的情况下,CORS是必然要解决的问题。那什么是CORS呢?
    CORS是跨域资源共享的英文单词缩写,CORS是浏览器的一种策略,出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求响应,比如在http://127.0.0.1:8080的页面上向http://127.0.0.1:80通过ajax发出一个post请求,此时80端口后端可以收到http请求,但浏览器会主动拦截响应结果。

    那么怎么解决跨域问题呢,有两个方案

    1. 通过后端服务器设置正确的响应头,来告诉浏览器我允许这次跨域,请求浏览器放行
    2. JSONP

    本文主要使用第一个方案,所以先简单讲讲JSONP,大家肯定都用过script标签,一些常用的前端框架如jquery,我们可以直接使用对方提供的url,浏览器也不会拦截这些内容,也就是说浏览器对script标签的url是不存在跨域访问控制的,基于这一特性,我们将一个跨域请求包装在script标签内,这样就可以规避CORS的拦截。那么问题在于我通过script请求获得的数据要如何操作,通常的操作是通过定义回调函数名,后端将回调函数和实际返回的数据做字符串拼接,前端收到数据后直接执行前端定义好的方法。

    举个栗子 我有一个api http://127.0.0.1:8080 正常响应的内容{'msg': 'hello'} 允许传入一个参数callback 假如callback=printf 那么响应内容变成printf({'msg': 'hello'})
    前端域名http://127.0.0.1 现在向访问前面的api 弹出msg对应的内容 那么在前端使用jsonp的方式应该是这样的

    <script>
    function printf(data) {
        alert(data.msg)
    }
    </script>
    // 通过js代码生成一个这样的<script>标签
    <script src="http://127.0.0.1:8080?callback=printf"></script>
    

    所以JSONP的核心就是基于script的特性,通过前端的回调和后端的字符串拼接来避过CORS策略。
    JSONP的问题在于scipt标签只能get请求,不能解决post、put等其他请求方式的问题

    JSONP是通过另辟蹊径来绕过CORS,那么有没有一种直面CORS请求浏览器放行的策略呢,当然也是有的
    我们可以通过响应头来告诉浏览器,这个响应允许某个域接受,如果你碰到了这个域你就放行吧,关于CORS所规范的响应头如下(参考资料

    • Access-Control-Allow-Origin
      参数的值指定了允许访问该资源的外域 URI,服务器可以指定该字段的值为通配符"*"
    • Access-Control-Expose-Headers
      在跨域访问时,XMLHttpRequest对象的getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
    • Access-Control-Max-Age
      指定了预检请求的结果能够被缓存多久
    • Access-Control-Allow-Credentials
      指定了当浏览器的credentials设置为true时是否允许浏览器读取response的内容
    • Access-Control-Allow-Methods
      用于预检请求的响应,其指明了实际请求所允许使用的 HTTP 方法
    • Access-Control-Allow-Headers
      用于预检请求的响应,其指明了实际请求中允许携带的首部字段

    简单说说前面没有聊到的预检概念:
    浏览器将请求分成了简单请求和复杂请求两种类别,对于复杂请求,浏览器首先会有一个"预检"的行为,具体而言就是先向url发起一次OPTIONS请求,这个请求被称为预检。后端收到预检请求后,应该响应前面规定的几个响应头,来告诉浏览器后端允许的一些跨域策略。

    到现在,要解决跨域问题就比较明朗了,前端不需要任何操作,后端注意两个点即可

    • 实现OPTIONS预检请求响应
    • 在正常请求响应中添加Access-Control-Allow-Origin响应头

    回到应用,现在就以Sanic为例,来看看如何解决跨域(逐渐想起标题...)

    首先在Sanic的生命周期中,有一点我是比较失望的,流程大致如下:
    http请求——Sanic解析request——匹配路由——请求中间件——视图函数——响应中间件——http响应

    Sanic在匹配路由中会检测是否存在对应的请求方法,如果没有直接响应405,根本不走后面的中间件了,这意味着你不能使用中间件来实现所有路由表上的预检请求,那么在注册路由的时候,就一定要显式申明OPTIONS或在CBV中实现options方法,之后我们才可以通过中间件来实现功能

    @app.middleware("request")
    def cors_middle_req(request: Request):
        """路由需要启用OPTIONS方法"""
        if request.method.lower() == 'options':
            allow_headers = [
                'Authorization',
                'content-type'
            ]
            headers = {
                'Access-Control-Allow-Methods':
                    ', '.join(request.app.router.get_supported_methods(request.path)),
                'Access-Control-Max-Age': '86400',
                'Access-Control-Allow-Headers': ', '.join(allow_headers),
            }
            return HTTPResponse('', headers=headers)
    
    @app.middleware("response")
    def cors_middle_res(request: Request, response: HTTPResponse):
        """跨域处理"""
        allow_origin = '*'
        response.headers.update(
            {
                'Access-Control-Allow-Origin': allow_origin,
            }
        )
    

    前一个请求中间件cors_middle_req用于拦截所有OPTIONS方法,它在设置三个跨域请求头后直接返回HTTPResponse对象,在这个中间件中,告知了浏览器允许的请求方式和请求头,并设置了一个24小时的缓存(86400/3600)时间
    后一个响应中间件cors_middle_res用于处理所有响应请求,在响应头中加入Access-Control-Allow-Origin来允许跨域,allow_origin可以改成指定域名
    在整个过程中我们使用到了6个规范响应头中最常用的4个,剩下两个响应头大家可以自行去了解用途和适用场景

    至此就解决了Sanic的跨域问题,整套流程梳理起来,当你了解了相关规范后其实你会发现并不复杂,任何web框架都可以简单的处理跨域问题。

  • 相关阅读:
    删除CSDN上传图片水印
    Win10任务栏中隐藏/恢复日期显示
    使用idea和gradle编译spring5源码
    错误:找不到或无法加载主类
    判断字符串是否为数字
    mysql根据json数据过滤
    mysql当不存在时插入
    org.apache.xerces.parsers.SAXParser
    mybatis mapper判断if条件写法
    《Java面向对象编程》
  • 原文地址:https://www.cnblogs.com/lazyfish007/p/13699745.html
Copyright © 2011-2022 走看看