聊聊Ajax跨域

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊Ajax跨域相关的知识,希望对你有一定的参考价值。

前言 

  由于浏览器的同源策略,使得发送请求的时候,只能给本域发送请求,不能跨域发送请求。对于Ajax请求来说,跨域浏览器是不允许的。原因不是不能发送,Ajax跨域的时候,确实发送了请求,并且收到了另一个域名的响应。但是浏览器会报错,默认情况下,Ajax不允许跨域,而script,img,  iframe 允许跨域,  基本上通过src引用去发送请求的,都可以跨域。src请求相当于发送了一个GET请求

  那么如何让Ajax可以跨域请求呢?今天介绍两种方案

JSONP

  什么是jsonp?jsonp是如何实现跨域请求的呢?

  在前言中提到,对于Ajax提交的请求是不允许跨域,只能是本域请求,而浏览器允许具有src属性的元素跨域进行请求。那么是否可以借助src属性来帮助Ajax跨域呢?

  答对了,这就是jsonp实现跨域请求的基本原理

  优点:兼容性好,应用广泛

  缺点:由于是src引用发送的请求,那么jsonp的请求只能是get请求,不能是其他的请求。

  好了,说到这里,我们来看看,jsonp是如何跨域的

技术分享
 1 # Python write by yhy
 2 import tornado.web
 3 import tornado.ioloop
 4 
 5 class IndexHandler(tornado.web.RequestHandler):
 6     def get(self, *args, **kwargs):
 7         self.write(‘func([11,22,33])‘)
 8 
 9 # 说明一下这个‘static_url_prefix‘: ‘/statics/‘,的问题
10 settings = {
11     ‘template_path‘: ‘views‘,
12     ‘static_path‘: ‘statics‘,
13     ‘static_url_prefix‘: ‘/statics/‘,
14 }
15 
16 application = tornado.web.Application([
17     (r‘/index‘, IndexHandler)
18 ], **settings)
19 
20 if __name__ == ‘__main__‘:
21     application.listen(8002)
22     tornado.ioloop.IOLoop.instance().start()
服务端tornado框架代码
技术分享
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <script src="/statics/jquery.js"></script>
 7 </head>
 8 <body>
 9 <input type="button" value="Ajax" onclick="DoAjax();">
10 <input type="button" value="JsonpAjax" onclick="DoJsonpAjax();">
11 <script>
12     function func(arg) {
13         console.log(arg)
14     }
15 
16     // Ajax
17     function DoAjax() {
18         $.ajax({
19             url: http://127.0.0.1:8002/index,
20             type: POST,
21             data: {k1: v1},
22             success: function (arg) {
23                 console.log(arg);
24             }
25         })
26     }
27 
28     // JsonAjax
29     function DoJsonpAjax() {
30         var tag = document.createElement(script);
31         tag.src = "http://127.0.0.1:8002/index";
32         document.head.appendChild(tag);
33         document.head.removeChild(tag);
34     }
35 </script>
36 </body>
37 </html>
原生的AjaxJSONP
技术分享
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <script src="/statics/jquery.js"></script>
 7 </head>
 8 <body>
 9 <input type="button" value="Ajax" onclick="DoAjax();">
10 <input type="button" value="JsonpAjax" onclick="DoJsonpAjax();">
11 <script>
12     // 这里的func函数就是等待被调用,等待跨域请求返回的调用函数的字符串调用,一旦调用就会在这个函数中拿到跨域请求的参数。
13     function func(arg) {
14         console.log(arg)
15     }
16 
17     // Ajax
18     function DoAjax() {
19         $.ajax({
20             url: http://127.0.0.1:8002/index,
21             type: POST,
22             data: {k1: v1},
23             success: function (arg) {
24                 console.log(arg);
25             }
26         })
27     }
28 
29     // JsonAjax
30     function DoJsonpAjax() {
31         // 基于jquery的Ajax的跨域请求,只要指明dataType为‘jsonp‘,那么就相当于创建了一个带有src的script标签,跨域请求通常返回一个函数,这个函数包裹在<script> func(‘hello world’) <script> 标签里面。一旦返回到html页面的时候,将会调用提前在<script> <script> 标签中写好的函数。这个写好的<script> <script>标签中写好的函数就会拿到函数调用传递进来的参数‘hello world’。这样就实现了跨域的请求
32         $.ajax({
33             url: http://127.0.0.1:8002/index,
34             dataType: jsonp,
35             jsonpCallBack: func
36 
37         })
38     }
39 </script>
40 </body>
41 </html>
基于jQuery的AjaxJSONP

 

  ** 思考:服务端的返回的func函数名称是否可以与客户端提前写好的回调函数名一致呢,如果一致的话,那么就不需要在服务端指定函数名了,客户端的回调函数叫什么名字,服务端就叫什么名字就好。
  解决的方式:服务端拿到请求的url的参数,这个参数里面有一个callback参数,这个callback参数的值就是客户端请求的函数名称
  那么服务端返回的代码就可以这么写:通过self.get_argument(‘callback‘)拿到客户端的回调函数名
技术分享服务端拿到返回的函数名
技术分享
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 
 7     <script src="../statics/jquery.js"></script>
 8 </head>
 9 <body>
10 <input type="button" onclick="AjaxJsonp();" value="Ajax" >
11 <input type="button" onclick="jQueryAjaxJsonp();" value="jqueryAjax">
12 
13 <script>
14     function getData(arg) {
15         console.log(arg);
16     }
17     function AjaxJsonp() {
18         var tag = document.createElement(script);
19         tag.src = http://yhycn.com:8002/index?callback=getData;
20         document.head.appendChild(tag);
21         document.head.removeChild(tag);
22     }
23     function jQueryAjaxJsonp() {
24         $.ajax({
25             url: http://yhycn.com:8002/index,
26             dataType: jsonp,
27             // 下面的jsonp: ‘callback‘,jsonCallBack: ‘getData‘, 相当于在请求的url中指定/index?callback=getData
28             jsonp: callback,
29             jsonCallback: getData,
30         })
31     }
32 </script>
33 </body>
34 </html>
前端html代码

 

 

CORS:跨域资源共享(Cross-Origin Resource Sharing)

  当客户端跨域请求时,服务端返回数据的时候,同时绑定一个标记,告诉浏览器这是跨域请求的数据,不要报错。当浏览器识别这个标示的时候,浏览器就不会报错了,这个表示就是加上一个响应头CORS。这样就实现了跨域请求。客户端请求与本域请求一样不变,服务端这是一个CORS响应头即可。

  优点:客户端不需要修改原本的ajax跨域请求,只是服务端需要进行相应的设置即可
  缺点:兼容性不好,由于IE浏览器对跨域资源共享的兼容性问题,因此cors应用不是很广泛

  概念说明:

条件:

    1、请求方式:HEAD、GET、POST

    2、请求头信息:
        Accept
        Accept-Language
        Content-Language
        Last-Event-ID

        Content-Type 对应的值是以下三个中的任意一个
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain

注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

1:简单的cors请求
     对应简单的cors请求而言,与普通的本域请求一样,而服务端需要在set-header中设置运行的请求客户端地址
2:复杂的cors请求
     对应复杂的cors请求而言,与普通的本域请求不一样的地方在于,请求方法可以是head, get , post之外的其他方法,可以自定义请求头,可以告诉客户端浏览器请求的时候携带cookie。客户端会先发送一个options自检请求,在发送一个type中指定的请求
     而服务端对复杂的cors请求,首先会处理自检请求,在处理type中指定的请求

 

技术分享
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <script src="/statics/jquery.js"></script>
 7 </head>
 8 <body>
 9 
10 <input type="button" onclick="jQueryAjax();" value="jqueryAjax">
11 <input type="button" onclick="jQueryAjaxComplecate();" value="jQueryAjaxComplecate" >
12 <script>
13     // TODO 简单的CORS请求, 请求的方法为 POST
14     function jQueryAjax() {
15         $.ajax({
16             url: http://yhycn.com:8002/cors,
17             type: POST,
18             data: {k1: v1},
19             success: function (arg) {
20                 console.log(arg)
21             }
22         })
23     }
24 
25     // TODO 复杂的cors, 请求的方式不是HEAD、GET、POST中的一种,满足复杂请求的要求, 可以自定义请求头,是否携带cookie
26     function jQueryAjaxComplecate() {
27         $.ajax({
28             url: http://yhycn.com:8002/cors,
29             type: PUT,
30             // 设置一个请求头,自定义请求头, 自定义了headers也是属于复杂的请求,因此需要在后台允许这样的headers
31             headers: {h1: hh1},
32             // 指定在客户端跨域发送请求的时候,携带cookie
33             xhrFields:{withCredentials: true},
34             data: {k1: v1},
35             success: function (arg) {
36                 console.log(arg);
37             }
38         })
39     }
40 </script>
41 </body>
42 </html>
请求代码
技术分享
 1 # Python write by yhy
 2 # Python write by yhy
 3 import tornado.web
 4 import tornado.ioloop
 5 
 6 class IndexHandler(tornado.web.RequestHandler):
 7     def get(self, *args, **kwargs):
 8         self.write(‘hello‘)
 9 
10 class CorsHandler(tornado.web.RequestHandler):
11     def get(self, *args, **kwargs):
12         self.write(‘{"status": 1, "message": "get"}‘)
13 
14     def post(self, *args, **kwargs):
15         # 服务端设置一个允许跨域的响应头,响应头的key=‘Access-Control-Allow-Origin‘, value=客户端的域名, * 表示所有的客户端都可以
16         self.set_header(‘Access-Control-Allow-Origin‘, ‘*‘)
17         self.write(‘{"status": 1, "message": "post"}‘)
18 
19     # 这个是浏览器自动发送的域检请求
20     def options(self, *args, **kwargs):
21         # 指定允许客户端发送PUT请求
22         self.set_header(‘Access-Control-Allow-Origin‘, ‘http://yhy.com:8001‘)
23         self.set_header(‘Access-Control-Allow-Methods‘, ‘PUT, DELETE‘)
24         # 允许自定义的请求头通过
25         self.set_header(‘Access-Control-Allow-Headers‘, ‘h1, h2‘)
26         self.set_header(‘Access-Control-Allow-Credentials‘, ‘true‘)
27 
28     def put(self, *args, **kwargs):
29         # 给客户端设置cookie值,当客户端请求的时候,会携带cookie值,
30         # TODO 应该注意的是,如果客户端跨域请求的时候携带了cookie值的话,那么需要将Access-Control-Allow-Origin的值设置为客户端的域名地址URL
31         print(self.cookies)
32         self.set_cookie(‘k1‘, ‘kkkk‘)
33         self.set_header(‘Access-Control-Allow-Origin‘, ‘http://yhy.com:8001‘)
34         self.set_header(‘Access-Control-Allow-Credentials‘, ‘true‘)
35         self.write(‘put‘)
36 
37 settings = {
38     ‘template_path‘: ‘views‘,
39     ‘static_path‘: ‘statics‘,
40     ‘static_url_prefix‘: ‘/statics/‘,
41 }
42 
43 application = tornado.web.Application([
44     (r‘/index‘, IndexHandler),
45     (r‘/cors‘, CorsHandler),
46 ], **settings)
47 
48 if __name__ == ‘__main__‘:
49     application.listen(8002)
50     tornado.ioloop.IOLoop.instance().start()
服务端响应代码

 

 





以上是关于聊聊Ajax跨域的主要内容,如果未能解决你的问题,请参考以下文章

ajax技术和跨域问题

PHP中运用jQuery的Ajax跨域调用实现代码

jQuery跨域ajax:执行时回调

如何发送跨域ajax请求[重复]

关于ajax跨域

如何解决ajax跨域问题