服务器端代码如下:
import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class Invoke extends HttpServlet{ @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { try { String jsons="[{\"addTime\":\"2011-09-19 14:23:02\",\"iccid\":\"1111\",\"id\":0,\"imei\":\"2222\",\"imsi\":\"3333\",\"phoneType\":\"4444\",\"remark\":\"aaaa\",\"tel\":\"5555\"}," + "{\"addTime\":\"2011-11-11 14:23:02\",\"iccid\":\"2222\",\"id\":0,\"imei\":\"2222\",\"imsi\":\"3333\",\"phoneType\":\"4444\",\"remark\":\"aaaa\",\"tel\":\"5555\"}]"; resp.setContentType("text/javascript"); boolean jsonP = false; String cb = req.getParameter("pursue"); System.out.println(cb); if (cb != null) { jsonP = true; resp.setContentType("text/javascript"); System.out.print("js"); } else { //如果没有这个参数表示直接返回JSON数据就可以了 resp.setContentType("application/x-json"); System.out.print("json"); } resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter(); if (jsonP) { //如果是JSON的数据格式,那么对这个jsonObject进行tostring操作 //因为这个action没有返回物理视图,直接可以通过response对象进行返回数据 //返回类型 callbackfunction(字符串) out.write(cb + "("+jsons+")"); } else{ out.write("出错"); } }catch (Exception e) { e.printStackTrace(); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub doGet(req,resp); } }note:jsonp的出现是为了实现跨域资源共享的,这是在CORS出现之前的常用做法。他的本质是通过动态script来完成的,因为script有能力不受限制的从其它域中加载资源。
第一种方式:(通过script标签来实现)
window.onload=function() { //回调函数 var url = "http://127.0.0.1:8080/sealJs/jsonp?pursue=callbackfunction"; var script = document.createElement('script'); script.setAttribute('src', url); //load javascript document.getElementsByTagName('head')[0].appendChild(script); } function callbackfunction(data){ alert(data[0].addTime); }note:这是通过script标签,也就是标准的jsonp来完成的,但是很显然我们必须弄清楚,我们访问了服务器端的代码,必须通过服务器端明确指定返回来的是javascript类型才可以。服务器端核心代码:
String cb = req.getParameter("pursue"); resp.setContentType("text/javascript"); out.write(cb + "("+jsons+")");note:服务器端先获取到回调函数callbackfunction,然后把要返回的json数据作为这个函数的参数,最后通过Content-Type告诉浏览器这是javascript类型, 也就是告诉浏览器这是可以直接执行的代码!通过chorme开发者工具,我们看到服务器返回的脚本已经被添加到head中了,不过这种回调是通过服务器来完成的,而不是通过 onload等事件来检测的。缺点:从其它域加载文件,如果其它于不安全,那么会带来恶意的代码;要确定jsonp请求失败不容易,虽然HTML5提供了script的onerror,但是浏览器支持并不好。
$(document).ready(function() { $.ajax({ type:"GET", url:"http://127.0.0.1:8080/sealJs/jsonp", jsonp:"pursue", jsonpCallback:"backInvoke", cache:false, dataType:"jsonp" }); }); function backInvoke(data) { console.log(data[0].addTime); }note:这是通过ajax方法来完成的,但是这种方式和方式1,也就是动态创建script有什么区别? 答案是没有区别,jquery是通过 ajaxTransport来完成script的类型的加载的,他也是通过动态创建script来完成的!
$(function(){ /*(1)因为getJSON底层调用的是ajax方法,所以这里不能明确指明回调函数jsonpCallback,否则就会去回调他 但是getJSON没有这种用法 (2)虽然底层调用了ajax方法,但是无法明确指定是jsonp请求,所以服务器端不会处理,所以这种情况下要跨域 必须给服务器添加ACCESS-CONTROL-ALLOW-ORIGION头 */ jQuery.getJSON("http://127.0.0.1:8080/sealJs/jsonp?pursue=?",function(data) { console.log(data[0].addTime); }); });note:这里要必须注意,虽然底层调用了ajax方法, 但是我们无法指定jsonp请求,所以这种情况下要跨域必须具有access-control-allow-origion头部。
function xx(data) { console.log(data[0].addTime); } $(function() { $.getScript("http://127.0.0.1:8080/sealJs/jsonp?pursue=xx"); })
note:底层仍然调用ajax方法,不过dataType是script,因此ajaxTransport中的处理不需要在服务器端明确指定access-control-allow-origion,因为script本来就可以跨域获取数据,所以他和第一种方式是完全相同的,和ajax方法也是完全相同的!但是getJSON方式调用ajax时候指定的dataType是"json",因此他无法实现跨域,所以要借助access-control-allow-origion!
CORS默认情况下是不发送凭证的,我们有时候需要发送凭证怎么处理:
$(function() { $.ajax({ url:"http://localhost:8080/CORS/cors",//这时候也必须指定access-control-allow-origion,因为我使用了虚拟地址,用的localhostme的域名进行访问的! xhrFields: { withCredentials: true//表示发送凭证,但测试结果表示只会发送jsessionid,普通的cookie不会发送! } }); })note:这时候不管服务器端有没有access-control-allow-credential都会发送凭证给服务器, 但是经过测试,发送的数据只是服务器的session的cookie对象,而其它的cookie不会发送。服务器能够收到JSESSIONID,但是其它的cookie不会收到。如果服务器接受带凭证的请求,必须发送access-control-allow-origion,否则浏览器不会把数据交给javascript,结果就是responseText为空字符串,status为0,调用onerrror事件处理程序!
一个具有多个"字段名称-字段值"对的对象,用于对本地XHR对象进行设置。一对「文件名-文件值」在本机设置XHR对象。例如,如果需要,你可以用它来为跨域请求设置XHR对象的withCredentials
属性为true
。注意:指定了这个头以后会发送凭证sessionid,但是抓包工具并没有显示他!
默认值:{}
。
以对象形式指定附加的请求头信息。请求头X-Requested-With: XMLHttpRequest
将始终被添加,当然你也可以在此处修改默认的XMLHttpRequest值。headers
中的值可以覆盖beforeSend
回调函数中设置的请求头(意即beforeSend先被调用)。headers头将会添加如Access-Control-Request-Headers,Access-Control-Request-Method等头部信息。
(1)ajax方法指定dataType为jsonp,getScript的dataType是script,手动创建script加载远端数据这三个方法都是通过script可以跨域加载数据的天性来完成的。在服务器端通过返回数据类型为"text/javascript"来实现javascript代码在浏览器端直接执行!
(2)getJSON方法调用ajax方法时候指定的dataType是json,所以无法直接实现跨域,这时候就要借助于access-control-allow-origion来完成!
(3)带凭证的请求发送的方法为options方法,而不是get/post方法!当用户通过setRequestHeader自定义头部信息,浏览器默认先发送一个请求用于判断该请求是否合法,如果不通过ajax请求就直接失败了,如果通过了浏览器再次发送一个请求,读取服务器返回的数据。也就是options方法会进行两次请求。我们可以通过设置Access-control-max-age用于指定该请求缓存的时间,把请求的结果缓存起来,为此付出的第一次代价就是第一次请求的时候多发送了一次HTTP请求。通过setRequestHeader('X-Request-With', null)可以避免浏览器发送OPTIONS请求。