亲测CORS(跨域资源共享)

随着软件开发过程分工越来越细,前段开发和后端开发、移动端开发越来越分离,后端设计的RESTFul API需要被其他的应用访问,因浏览器的安全限制,在跨站点进行访问的时候会有安全限制;CORS解决不同的站点之间的资源共享问题,关于CORS可以参看维基百科

实验环境

站点1是一个HTML页面,可以理解为RESTFul API的消费端

<!DOCTYPE html>
<html>

<head>

</head>

<body>
    <h1>for test</h1>
    <script src="{{static_url('jquery-1.6.2.min.js')}}"></script>
    <script type="text/javascript">

        $(function () {
            //alert('aaaaaaa');
            $.ajax(
                {
                    url: "http://localhost:8082/api/test_cors",
                    success: function (res) {
                        alert("bbbbbb");
                    },
                });
            $.ajax(
                {
                    url: "http://localhost:8082/api/test_cors",
                    //data:JSON.stringify({a:"123",b:"345"}),
                    //contentType:"application/json",
					data:{a:"123",b:"345"},
                    type:'POST',
                    dataType: "json",
                    headers:{"x-token":"aaaaaaaaaasdfjsd;fajasd;fjsd;jfad;sfjasd;fjasd"},
                    success: function (res) {
                        alert("cccc");
                    },
                }); 
        });
    </script>
</body>
</html>

页面运行在http://localhost:8888下,功能是从另外一个站点http://localhost:8082/api/test_cors上进行GETPOST请求,进行POST请求分为两种情况

  1. content-type=='application/json'
  2. content-type=='application/x-www-form-urlencoded' #默认情况下,ajax都采用这种方式进行POST提交

站点2可以理解为一个 RESTFul API的提供者,提供的服务供其他的站点进行访问

代码如下:

# -*- coding:utf-8  -*- 

from tornado.gen import coroutine,IOLoop
from tornado.options import parse_command_line,options
from tornado.web import RequestHandler,Application ,escape
from tornado.log import gen_log

def main():
    parse_command_line()
    app=Application(handlers=[
        (r'/api/test_cors',TestService)
    ],debug=True)
    app.listen('8082')
    IOLoop.current().start()


class CORSHandler(RequestHandler):
    """
    跨站点请求的处理(Handler)器
    """    
    def options(self):
        gen_log.info('entry options method')
        #self._add_cors_request_headers()

    def _add_cors_request_headers(self):
        """
        添加跨站点请求的头信息
        """
        headers=self.request.headers
        origin=headers.get("Origin",None)
        allow_method=headers.get('Access-Control-Request-Method','')
        custome_header=headers.get('Access-Control-Request-Headers','')
        # output all follow to client
        self.add_header('Access-Control-Allow-Origin',origin or '*')
        self.add_header('Access-Control-Allow-Methods',','.join(self.SUPPORTED_METHODS))
        self.add_header('Access-Control-Allow-Headers',custome_header)
        self.add_header('Access-Control-Allow-Credentials',True)
        self.add_header('Access-Control-Max-Age',1728000)

    def get_current_user(self):
        """
        获取当前用户
        :return: 
        """
        return {
            "empCd":"12860",
            "pwd":"nothing"
        }

    @property
    def user(self):
        return self.current_user

    def write_json(self,data,code=0,err_msg=None):
        """"""
        #self._add_cors_request_headers()
        self.set_header("content-type","application/json")
        self.write({"data":data,"code":code,"err_msg":err_msg})


class TestService(CORSHandler):
    @coroutine
    def get(self):
        data = {
            "a": "a",
            "b": "b"
        }
        self.write_json(data)

    @coroutine
    def post(self):
        # todo if content-type is application/json
        #json_body=escape.json_decode(self.request.body)
        #a = json_body.get("a")
        #b = json_body.get("b")

        # todo if content-type is application/x-www-form-urlencoded
        a=self.get_argument('a')
        b=self.get_argument('b')
        self.write_json({"a": a, "b": b})

if __name__ =='__main__':
    main()

实验分为两部分

  • 简单请求的跨站点访问验证

这里的简单请求时指,请求的content-type是application/x-www-form-urlencoded和multipart/form-data

从代码来看浏览器应该会发送两个网路请求给服务器,如果服务器正确的响应了浏览器的请求则浏览器会弹出提示框, 上述代码运行效果如下:

实际上没有接收到浏览器的提示框,在控制台看到跨域的警告消息。为了解决跨域的警告,开启cors的支持。 将代码的self._add_cors_reuest_headers的注释取消掉

 def write_json(self,data,code=0,err_msg=None):
        """"""
        self._add_cors_reuest_headers() #开启cors的支持

开启cors的支持后,运行效果: 警告消失了 发出去的请求数是2

  • 非简单请求的跨站点访问验证

 非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。随着前后端开发的分离合前端开发的很多js框架发起的网路请求默认content-type都是application/json不是application/x-www-form-urlencoded.

修改html中的代码,使ajax进行post提交的时候,发送的content-type是application/json且表单的数据修改为json的格式,修改内容如下:

修改前:

					url: "http://localhost:8082/api/test_cors",
                    //data:JSON.stringify({a:"123",b:"345"}),
                    //contentType:"application/json",
					data:{a:"123",b:"345"},
					省略...

修改后:

					url: "http://localhost:8082/api/test_cors",
                    data:JSON.stringify({a:"123",b:"345"}),
                    contentType:"application/json",
					//data:{a:"123",b:"345"},
					省略...

刷新页面,运行效果如下: 提示有错误

从网络请求中可以看到,多了一个OPTIONS的请求,但是没有POST的请求。按照CORS的规范,非普通请求会预先发起一个OPTIONS请求给跨站点的资源,如果OPTIONS响应的内容允许访问则再次发起POST请求。出现OPTIONS请求正常但是POST请求没有发送的情况,可能的问题出来OPTIONS方法的代码。修改OPTIONS的代码:

修改前:

def options(self):
        gen_log.info('entry options method')
        #self._add_cors_request_headers()
@coroutine
    def post(self):
        # todo if content-type is application/json
        #json_body=escape.json_decode(self.request.body)
        #a = json_body.get("a")
        #b = json_body.get("b")

        # todo if content-type is application/x-www-form-urlencoded
        a=self.get_argument('a')
        b=self.get_argument('b')

修改后:

def options(self):
        gen_log.info('entry options method')
        self._add_cors_request_headers()
@coroutine
def post(self):
    # todo if content-type is application/json
    json_body=escape.json_decode(self.request.body)
    a = json_body.get("a")
    b = json_body.get("b")

    # todo if content-type is application/x-www-form-urlencoded
    #a=self.get_argument('a')
    #b=self.get_argument('b')

刷新页面查看运行效果:

正常的访问了其他站点上的资源,以前的过程一直有一个核心的方法在起作用,可以看看:

def _add_cors_request_headers(self):
        """
        添加跨站点请求的头信息
        """
        headers=self.request.headers
        origin=headers.get("Origin",None)
        allow_method=headers.get('Access-Control-Request-Method','')
        custome_header=headers.get('Access-Control-Request-Headers','')
        # output all follow to client
        self.add_header('Access-Control-Allow-Origin',origin or '*')
        self.add_header('Access-Control-Allow-Methods',','.join(self.SUPPORTED_METHODS))
        self.add_header('Access-Control-Allow-Headers',custome_header)
        self.add_header('Access-Control-Allow-Credentials',True)
        self.add_header('Access-Control-Max-Age',1728000)

详细的解释参看阮一峰的网络日志

小结:

CORS很好的解决了前后端分离开发时的跨站点请求问题,让前后端协同更加顺利。当前后端分离开发后也可以采用类似NGINX反向代理的方案来解决API的调用问题,但CORS配置更少,更透明。遗憾的是目前在IE浏览器上IE9以下都不支持CORS。

参考链接:

转载于:https://my.oschina.net/hulingfeng/blog/2051255

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值