服务器端代码如下:
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.οnlοad=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,但是浏览器支持并不好。
第二种方式:(通过jQuery本身的ajax方法来完成)
$(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来完成的!
第三种方法:通过getJSON来跨域:
$(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头部。
第四种方法:通过getScript来跨域
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事件处理程序!
xhrFieldsObject类型1.5.1 新增
一个具有多个"字段名称-字段值"对的对象,用于对本地XHR对象进行设置。一对「文件名-文件值」在本机设置XHR对象。例如,如果需要,你可以用它来为跨域请求设置XHR对象的withCredentials
属性为true
。注意:指定了这个头以后会发送凭证sessionid,但是抓包工具并没有显示他!
headersObject类型1.5 新增
默认值:{}
。
以对象形式指定附加的请求头信息。请求头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请求。