跨域Ajax JSONP 及 CORS
由于浏览器存在同源策略机制,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性。
特别的:由于同源策略是浏览器的限制,所以请求的发送和响应是可以进行,只不过浏览器不接受罢了。
浏览器同源策略并不是对所有的请求均制约:
- 制约: XmlHttpRequest,
- 不叼: img、iframe、script等具有src属性的标签
跨域,跨域名访问,如:http://www.c1.com 域名向 http://www.c2.com域名发送请求。
JSONP实现跨域请求
JSONP(JSONP - JSON with Padding是JSON的一种“使用模式”),利用script标签的src属性(浏览器允许script标签跨域)
JSONP ------>注意一点:发送的是GET请求 是一种技巧或是说技术, Ajax存在一个问题:访问自己域名URL的请求是没有任何问题的,但是访问其他域名URL的时候,浏览器会做出相应的操作,请求就会被阻止。 产生原因: 浏览器在当前页面给第三方发送请求,数据也从服务器发会给浏览器,但是由于浏览器的同源策略,数据被浏览器禁止了,返回值就接收不到。 由于这种原因,浏览器判定只能给当前的url发送请求,不能跨域。 浏览器:同源策略 - 禁止:Ajax跨域发送请求时,再回来时浏览器拒绝接收。 - 允许:img,script标签没禁止 浏览器不会阻止src=""的加载。 处理方法:绕过浏览器的同源策略(钻空子),既然禁止ajax跨域,但是允许带有src属性标签进行跨域。 所以就不再用ajax去发送请求而是创建一个有这种属性的标签,然后把跨域请求的url写到src属性下,然后把这个带有数据的标签加到当前页面上。 例如利用script便签,然后请求返回的内容就会交给script进行解析。 利用src发送请求,就会把访问的页面内容完全下载下来,保存在内存当中。若是有对应的加载策略,页面一刷新就能把型对应的数据在页面上显示出来。 JSONP:钻空子 # http://www.baidu.com?p=1&name=xx 0. function list(arg){ console.log(arg); } 1. 发送:把url地址放在标签中 把数据拼接成,script放在html <script src='http://www.jxntv.cn/data/jmd-jxtv2.html?callback=list&_=1454376870403'></script> 返回的是list({......})函数,当前页面也得有一个相对应的调用函数,数据一返回就能执行这个调用的函数,把数据加载显示出来。 要求:远程返回值必须是一个有参数的函数,在当前本地执行这个函数就能获取到数据,所以我们都需要遵守这个规则。 利用js 可以被浏览器加载的特性。 当作js代码放到浏览器所属客户端的内存中 跨域请求,获取其他网站的数据方法: 第一种:浏览器向自己服务端发送请求,获取到数据之后,再向其他网站的服务端发送请求,接收该服务端的返回的数据。这种方式需要考虑同源策略的问题。 第二种:浏览器向自己的服务端发送请求,然后服务端再向要其他的服务端发送请求,获取到数据,再把数据传递给浏览器。这种方式就不用考虑同源策略,也不会用到JSONP跨域操作了。 注意:服务端不是浏览器,访问任意网站发送请求,都会拿到返回的数据。 开发需求:向其他网站发Http请求 - 浏览器直接发送请求【考虑同源】 - 浏览器->服务端->发送请求 API:别人给提供的路径或是方法,通过这个方法就能获取到对应的数据。 浏览器直接发送请求【考虑同源】 注意:同源策略是浏览器做的策略,只要浏览器发送请求,服务端就会把数据返回给客户端存放于内存中,能不能渲染还是浏览器说了算。 要求:客户端和服务端协议必须达成一致,才可以实现跨域请求的操作! 1. 客户端 - URL?callback=xxxx 服务端以GET形式发送请求,把函数名放在url中 - function xxxx(arg){} 定义一个针对这个请求的自执行函数,用于加载后台返回的数据。 2. 服务端 - 获取 funcname = request.GET.get(callback) 获取GET请求 url 中自定义的函数名 - 返回: funcname(....) 以一个函数带数据的形式返回给浏览器 Jquery内部已经封装了JSONP的方法,所以需要跨域的话,jquery内部也能实现操作,直接就使用jquery的对应方法即可。 JSONP编写模版: 后台发送返回的数据,做成一个可操作的对象返回。浏览器前端接收到数据,然后利用js加载显示获取数据。 注意: 变量名要在浏览器中自己定义或命名,后台不用管直接对数据拼接操作就行,然后返回给浏览器。 浏览器要自定义一个函数执行这个操作,只要后台把数据返回,就自动执行对应的函数,把数据显示。 使用:分为两种方式: 1. 自己动态创建script标签,src添加url地址,定制加载函数。 function getUsers(){ var tag = document.createElement('script'); tag.src = "http://www.s4.com:8001/users/?funcname=bbb?sdd"; document.head.appendChild(tag); } 2. 直接利用jQuery ajax请求实现跨域操作,也需要自定义加载函数。 $.ajax({ url: 'http://www.s4.com:8001/users/', 跨域要访问的地址 type: 'GET', 请求方式,必须是GET 换成了其他方式也会以GET方式请求。 dataType: 'JSONP', 指定参数,规定那种方法操作发送数据 jsonp: 'funcname', GET请求名 jsonpCallback: 'bbb' GET请求发送给后端的值 }) jquery ajax以JSONP技术向后台发送GET请求,上段代码会内部处理拼接成一个新的url地址,即:http://www.s4.com:8001/users/?funcname=bbb 发给服务端。 总结: 难点:怎么从内存中拿取返回的数据。所以需要按照一定的规则来传递数据。 注意点: - 浏览器只能发GET请求 - 浏览器与服务端之间必须约定好方法。参数一定是动态的!!! JSONP是一种方式,目的解决跨域问题。每一种语言中都有。
除JSONP外其他的跨域操作!修改配置,让浏览器的同源策略失效,所有的浏览器访问都能接收到数据。
CORS
随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing),其本质是设置响应头,相当于是通行证,使得浏览器允许跨域请求。
CORS --->自认为是高端操作:跨域资源共享 返回数据在响应头中加值,让浏览器放弃同源策略不在阻止。 随着技术的发展,现在的浏览器可以支持主动设置从而允许跨域请求,即:跨域资源共享(CORS,Cross-Origin Resource Sharing), 其本质是设置响应头,相当于是通行证,使得浏览器允许跨域请求。 内部存在机制,判断是什么请求,若是简单请求直接把数据返回。复杂的请求,会发两次请求,第一次是自动发送的预检请求option,发送的内容没有数据, 后台预检完毕,才会再发送带数据的操作请求。 简单请求: def new_users(request): obj = HttpResponse('返回内容') obj['Access-Control-Allow-Origin'] = "*" #设置允许跨域操作的响应头,值为允许访问的url地址。*代表全部。 return obj 复杂请求: 两次请求: 第一次:自动发送预检请求,没有数据,后台做检测允不允许这个操作。 第二次:允许就发送带数据的请求操作。 def new_users(request): if request.method == "OPTIONS": obj = HttpResponse() obj['Access-Control-Allow-Origin'] = "*" obj['Access-Control-Allow-Methods'] = "DELETE" return obj obj = HttpResponse('asdfasdf') obj['Access-Control-Allow-Origin'] = "*" return obj 注意点:两次请求,都需要浏览器允许该操作!即需要添加响应头信息。 其他: - 任何请求 详情参见 老司机博客 http://www.cnblogs.com/wupeiqi/articles/5703697.html
练习
JSONP跨域请求示例,代码如下:
s4day79项目:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="/static/js/jquery-3.2.1.js"></script> </head> <body> <input type="button" value="获取用户列表" onclick="getUsers();"> <ul id="user_List"> </ul> <script> //生成一个script标签,src是要请求的地址,进行跨域操作 {# function getUsers() {#} {# var tag = document.createElement("script");#} {# tag.src = "http://www.s4day80.com:8001/users/?funcname=bbb";#} {# document.head.appendChild(tag);#} {# }#} //数据返回后,自动执行自定义的函数,从内存中获取返回的数据 {# function bbb(arg) {#} {# console.log(arg);#} {# }#} //ajax发送跨域请求操作 function getUsers() { // XMLHttpRequest ----->ajax发送请求,浏览器同源策略直接阻止。 {# $.ajax({#} {# url: 'http://www.s4day80.com:8001/users/?funcname=bbb',#} {# type: 'GET',#} {# success: function (arg) {#} {# console.log(arg);#} {# }#} {# });#} // JSONP 给请求打上JSONP的标签,这样就不会被阻止。 $.ajax({ url: 'http://www.s4.com:8001/users/', type: 'POST', dataType: 'JSONP', jsonp: 'funcname', jsonpCallback: 'bbb' }) } //同上 function bbb(arg){ console.log(arg); } </script> </body> </html>
def jsonp(request): return render(request,"jsonp.html")
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^jsonp/', views.jsonp), ]
s4day80项目下:
import json from django.shortcuts import render,HttpResponse def users(request): v = request.GET.get("funcname") print("请求来了....") user_list = ["alex","eric","egon"] user_list_str = json.dumps(user_list,) temp = "%s(%s)"%(v,user_list_str,) print(temp,type(temp)) return HttpResponse(temp)
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^users/', views.users), ]
CORS跨域资源共享,示例代码:
s4day79项目:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <input type="button" value="获取用户列表" onclick="getUsers();" /> <ul id="user_list"> </ul> <script src="/static/jquery-1.12.4.js"></script> <script> function getUsers(){ $.ajax({ url: 'http://www.s4.com:8001/new_users/', type:"DELETE", success:function(arg){ console.log(arg); } }) } </script> </body> </html>
def cors(request): return render(request,'cors.html')
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^cors/', views.cors), ]
s4day80项目下:
def new_users(request): print(request.method) #简单的跨域请求 # user_list = [ # 'alex','eric','egon' # ] # user_list_str = json.dumps(user_list) # obj = HttpResponse(user_list_str) # # obj['Access-Control-Allow-Origin'] = "*" # return obj #复杂跨域请求 if request.method == "OPTIONS": obj = HttpResponse() obj['Access-Control-Allow-Origin'] = "*" obj['Access-Control-Allow-Methods'] = "DELETE" return obj obj = HttpResponse('asdfasdf') obj['Access-Control-Allow-Origin'] = "*" return obj
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^new_users/', views.new_users), ]