博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
解决AJAX跨域:1、利用JSONP;2、JS设置Header
阅读量:6673 次
发布时间:2019-06-25

本文共 6111 字,大约阅读时间需要 20 分钟。

一、利用JSONP:

  首先来看看在页面中如何使用jQuery的ajax解决跨域问题的简单版:

$(document).ready(function(){    var url='http://localhost:8080/WorkGroupManagment/open/getGroupById"       +"?id=1&callback=?';    $.ajax({        url:url,        dataType:'jsonp',        processData: false,         type:'get',        success:function(data){            alert(data.name);        },        error:function(XMLHttpRequest, textStatus, errorThrown) {            alert(XMLHttpRequest.status);            alert(XMLHttpRequest.readyState);            alert(textStatus);        }    });});

  这样写是完全没有问题的,起先error的处理函数中仅仅是alert(“error”),为了进一步弄清楚是什么原因造成了错误,故将处理函数变为上面的实现方式。最后一行alert反馈parsererror问题。百思不得其解,原因是jsonp的格式与json格式有着细微的差别,所以在server端的代码上稍稍有所不同。

  比较一下json与jsonp格式的区别:

  JSON格式:

{    "message":"获取成功",    "state":"1",    "result":{
"name":"工作组1","id":1,"description":"11"}}

  JSONP格式:

callback({    "message":"获取成功",    "state":"1",    "result":{
"name":"工作组1","id":1,"description":"11"}})

  看出来区别了吧,在url中callback传到后台的参数是什么callback就是什么,jsonp比json外面有多了一层callback()。这样就知道怎么处理它了,于是修改后台代码。

  后台java代码最终如下:

@RequestMapping(value = "/getGroupById")public String getGroupById(@RequestParam("id") Long id, HttpServletRequest request, HttpServletResponse response) throws IOException {    String callback = request.getParameter("callback");    ReturnObject result = null;    Group group = null;    try {      group = groupService.getGroupById(id);      result = new ReturnObject(group, "获取成功", Constants.RESULT_SUCCESS);    } catch (BusinessException e) {      e.printStackTrace();      result = new ReturnObject(group, "获取失败", Constants.RESULT_FAILED);    }    String json = JsonConverter.bean2Json(result);    response.setContentType("text/html");    response.setCharacterEncoding("utf-8");    PrintWriter out = response.getWriter();    out.print(callback + "(" + json + ")");    return null;}

  注意:这里需要先将查询结果转换为json格式,然后用参数callback在json外面再套一层,就变成了jsonp。指定数据类型为jsonp的ajax就可以做进一步处理了。

  虽然这样解决了跨域问题,还是回顾下造成parsererror的原因。原因在于盲目的把json格式的数据当做jsonp格式的数据让ajax处理,造成了这个错误,此时server端代码是这样的:

@RequestMapping(value = "/getGroupById")@ResponseBodypublic ReturnObject getGroupById(@RequestParam("id") Long id,HttpServletRequest request, HttpServletResponse response){    String callback = request.getParameter("callback");    ReturnObject result = null;    Group group = null;    try{        group = groupService.getGroupById(id);        result = new ReturnObject(group, "获取成功", Constants.RESULT_SUCCESS);    } catch (BusinessException e) {        e.printStackTrace();        result = new ReturnObject(group, "获取失败", Constants.RESULT_FAILED);    }    return result;}//错误处理方式

二、JS设置Header,实现跨域访问:

  受浏览器的同源策略限制,JavaSript只能请求本域内的资源。跨域资源共享(Cross-Origin Resource Sharing, CORS)是为解决Ajax技术难实现跨域问题而提出的一个,这个规范试着从根本上解决安全的跨域资源共享问题。在此之前,解决此类问题的途径往往是服务器代理、JSONP等,治标不治本。目前基本所有浏览器都已经该规范。

  一个域是由schema、host、port三者共同组成,与路径无关。所谓跨域,是指在http://example-foo.com/域上通过XMLHttpRequest对象调用http://example-bar.com/域上的资源。CORS约定服务器端和浏览器在HTTP协议之上,通过一些额外HTTP头部信息,进行跨域资源共享的协商。服务器端和浏览器都必需遵循规范中的要求。

  CORS把HTTP请求分成两类,不同类别按不同的策略进行跨域资源共享协商。

1. 简单跨域请求。

  当HTTP请求出现以下两种情况时,浏览器认为是简单跨域请求:

1). 请求方法是GET、HEAD或者POST,并且当请求方法是POST时,Content-Type必须是application/x-www-form-urlencoded, multipart/form-data或着text/plain中的一个值。

2). 请求中没有自定义HTTP头部。

  对于简单跨域请求,浏览器要做的就是在HTTP请求中添加Origin Header,将脚本所在域填充进去,向其他域的服务器请求资源。服务器端收到一个简单跨域请求后,根据资源权限配置,在响应头中添加Access-Control-Allow-Origin Header。浏览器收到响应后,查看Access-Control-Allow-Origin Header,如果当前域已经得到授权,则将结果返回给JavaScript,否则浏览器忽略此次响应。

2. 带预检(Preflighted)的跨域请求。

  当HTTP请求出现以下两种情况时,浏览器认为是带预检(Preflighted)的跨域请求:

1). 除GET、HEAD和POST(only with application/x-www-form-urlencoded, multipart/form-data, text/plain Content-Type)以外的其他HTTP方法。

2). 请求中出现自定义HTTP头部。

  带预检(Preflighted)的跨域请求需要浏览器在发送真实HTTP请求之前先发送一个OPTIONS的预检请求,检测服务器端是否支持真实请求进行跨域资源访问,真实请求的信息在OPTIONS请求中通过Access-Control-Request-Method Header和Access-Control-Request-Headers Header描述,此外与简单跨域请求一样,浏览器也会添加Origin Header。服务器端接到预检请求后,根据资源权限配置,在响应头中放入Access-Control-Allow-Origin Header、Access-Control-Allow-Methods和Access-Control-Allow-Headers Header,分别表示允许跨域资源请求的域、请求方法和请求头。此外,服务器端还可以加入Access-Control-Max-Age Header,允许浏览器在指定时间内,无需再发送预检请求进行协商,直接用本次协商结果即可。浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。这个过程对真实请求的调用者来说是透明的。

  XMLHttpRequest支持通过withCredentials属性实现在跨域请求携带身份信息(Credential,例如Cookie或者HTTP认证信息)。浏览器将携带Cookie Header的请求发送到服务器端后,如果服务器没有响应Access-Control-Allow-Credentials Header,那么浏览器会忽略掉这次响应。

  这里讨论的HTTP请求是指由Ajax XMLHttpRequest对象发起的,所有的CORS HTTP请求头都可由浏览器填充,无需在XMLHttpRequest对象中设置。以下是CORS协议规定的HTTP头,用来进行浏览器发起跨域资源请求时进行协商:

1. Origin。HTTP请求头,任何涉及CORS的请求都必需携带。
2. Access-Control-Request-Method。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的方法。
3. Access-Control-Request-Headers。HTTP请求头,在带预检(Preflighted)的跨域请求中用来表示真实请求的自定义Header列表。
4. Access-Control-Allow-Origin。HTTP响应头,指定服务器端允许进行跨域资源访问的来源域。可以用通配符*表示允许任何域的JavaScript访问资源,但是在响应一个携带身份信息(Credential)的HTTP请求时,Access-Control-Allow-Origin必需指定具体的域,不能用通配符。
5. Access-Control-Allow-Methods。HTTP响应头,指定服务器允许进行跨域资源访问的请求方法列表,一般用在响应预检请求上。
6. Access-Control-Allow-Headers。HTTP响应头,指定服务器允许进行跨域资源访问的请求头列表,一般用在响应预检请求上。
7. Access-Control-Max-Age。HTTP响应头,用在响应预检请求上,表示本次预检响应的有效时间。在此时间内,浏览器都可以根据此次协商结果决定是否有必要直接发送真实请求,而无需再次发送预检请求。

8. Access-Control-Allow-Credentials。HTTP响应头,凡是浏览器请求中携带了身份信息,而响应头中没有返回Access-Control-Allow-Credentials: true的,浏览器都会忽略此次响应。

  总结:只要是带自定义header的跨域请求,在发送真实请求前都会先发送OPTIONS请求,浏览器根据OPTIONS请求返回的结果来决定是否继续发送真实的请求进行跨域资源访问。所以复杂请求肯定会两次请求服务端。

  js 端的ajax请求:

$.ajax({     url: "http://test.com",     dataType: 'json',     type: 'GET',     beforeSend: function (xhr) {         xhr.setRequestHeader("Test", "testheadervalue");     },     async: false,     cache: false,     //contentType: 'application/x-www-form-urlencoded',     success: function (sResponse) {     }  });

  服务端的action:

//允许跨域访问  HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");  HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT");  HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Test");

总结:

1、JSONP格式:(1)url加:&callback=?;type变为"jsonp";(2)服务器端返回数据要为jsonp格式,即:out.print(callback + "(" + json + ")");要套一层callback()

2、服务器Action层增加Header头信息

转载地址:http://uzgxo.baihongyu.com/

你可能感兴趣的文章
Git上传代码的步骤
查看>>
sass基础常用指南
查看>>
数学 Codeforces Round #282 (Div. 2) B. Modular Equations
查看>>
select2清除选择(选择框内的值)
查看>>
超链接的学习与运用
查看>>
create-react-app按需引入antd-mobile
查看>>
webpack打包器
查看>>
车间不可操作非车间仓
查看>>
“亚信科技杯”南邮第七届大学生程序设计竞赛之网络预赛 G Prime [ 容斥原理 + 数论 + 求约数 + 素数筛 ]...
查看>>
【转载】cocos2d-x-3.0beta on android 打包错误问题
查看>>
JVM体系结构与工作方式
查看>>
如果编程语言是女人
查看>>
DensePose: Dense Human Pose Estimation In The Wild(理解)
查看>>
数据结构实验十——对称矩阵
查看>>
vs2010 快捷键大全
查看>>
清除缓存
查看>>
日历插件原理
查看>>
ASP.NET常见问题
查看>>
$watch
查看>>
管理信息系统的开发与管理
查看>>