zoukankan      html  css  js  c++  java
  • 处理跨域请求

    浏览器具有同源策略,会禁止向与当前页面不同的域发送请求,只要是协议,域名,端口中有任何一个不同,都被当作是不同的域,这虽然是一种保护数据的机制,但是对我们开发来说确是个麻烦,解决办法有很多,这里介绍一下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
    虽然复杂请求可以解决
    但是,要尽量避免复杂请求,会增加服务器压力
     
    还有一些设置:
      Access-Control-Max-Age    该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。
      Access-Control-Allow-Credentials      它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为true如果服务器不要浏览器发送Cookie,删除该字段即可。
     
      Access-Control-Expose-Headers    CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
     

     
    发送cookie
    CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,即指定Access-Control-Allow-Credentials :true
     
    另一方面,开发者必须在AJAX请求中打开withCredentials属性。
     
    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;

    否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理

    但是,如果省略withCredentials设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials   xhr.withCredentials = false;

     
    需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。
     
     

    总结,解决跨域请求,常用的有三种方式:requests模块,cors,jsonp
    后两种直接从前端拿数据,不需要经过服务器,requests模块是先把请求发送到服务器,服务器向目标发送请求,再返回数据到本地
     
    jsonp和cors相比,兼容性更好一点
     
    参考博客:http://www.cnblogs.com/wupeiqi/articles/5703697.html
        http://www.ruanyifeng.com/blog/2016/04/cors.html
  • 相关阅读:
    桟错误分析方法
    gstreamer调试命令
    sqlite的事务和锁,很透彻的讲解 【转】
    严重: Exception starting filter struts2 java.lang.NullPointerException (转载)
    eclipse 快捷键
    POJ 1099 Square Ice
    HDU 1013 Digital Roots
    HDU 1087 Super Jumping! Jumping! Jumping!(动态规划)
    HDU 1159 Common Subsequence
    HDU 1069 Monkey and Banana(动态规划)
  • 原文地址:https://www.cnblogs.com/zhang-can/p/7867750.html
Copyright © 2011-2022 走看看