最近一直在搞公众号前台开发, 遇到了 ajax 跨域请求的问题, 像地区的省 - 市 - 县三级联动汽车品牌 - 车系 - 车款的三级联动查询等都需要调用外部接口 (其他工程项目的接口) 完成下面就分享一下个人解决跨域请求的方案, 当然是在后台程序猿大哥的帮助下, 我才弄明白了其中的渊源, 赶紧记录下来慢慢积累, 也希望对大家能有所帮助, 还请积极提出意见或建议
跨域请求需要借助后台代码接收 callback 回调函数, 对 json 数据进行进一步处理; 前台再用 ajax 请求向服务器发送 callback 参数, 并指定数据格式为 jsonp
一后台对跨域请求进行处理
1.CarBrandController.java(汽车品牌接口 java 文件), 这里列出的方法主要用来根据不同的 level 值查询对应的品牌车系车款, 在这里对跨域请求做一个接收回调函数的处理, 如果返回的 callback 为 null, 则不是跨域请求, 不需要做特殊处理, 直接打印 json 接口数据即可; 如果返回的 callback 不为 null, 则表示跨域请求, 这时要对 json 数据做一个特殊处理, 即在 json 数据的外层加一对小括号包起来, 具体请看 HttpAdapter.java 文件中的 printlnJSONObject 方法
- public void json(HttpServletRequest request, HttpServletResponse response) {
- Map < String,
- Object > map = new HashMap < String,
- Object > ();
- String id = request.getParameter("id"); // 接收 ajax 请求带过来的 id
- String level = request.getParameter("level"); // 接收 ajax 请求带过来的 level
- String callback = request.getParameter("callback"); // 接收 ajax 请求带过来的 callback 参数
- if ("1".equals(level)) { // 如果 level 是'1', 则查询第一级目录内容
- map.put("results", this.carBrandService.findByAttr(null, "first_letter asc")); // 调用查询方法, 结果放入 map
- } else if ("2".equals(level)) { // 如果 level 是'2', 则查询第二级目录内容
- map.put("results", this.carSerieService.findByAttr("parent_id=" + id, "first_letter asc")); // 调用查询方法, 结果放入 map
- } else if ("3".equals(level)) { // 如果 level 是'3', 则查询第三极目录内容
- map.put("results", this.carModelYearService.findByAttr("parent_id=" + id, "jian_pin desc")); // 调用查询方法, 结果放入 map
- }
- map.put("level", level);
- if (null == callback) { // 如果接收的 callback 值为 null, 则是不跨域的请求, 输出 json 对象
- HttpAdapter.printlnObject(response, map);
- } else { // 如果接收的 callback 值不为 null, 则是跨域请求, 输出跨域的 json 对象
- HttpAdapter.printlnJSONPObject(response, map, callback);
- }
- }
2.HttpAdapter.java(输出对象的 java 文件),printlnObject 方法打印正常 json 字符串; printlnJSONObject 方法对 json 字符串进行了特殊处理
- /**
- * 打印对象
- * @param response
- * @param object
- */
- public static void printlnObject(HttpServletResponse response, Object object) {
- PrintWriter writer = getWriter(response);
- writer.println(JSON.toJSONString(object));
- }
- /**
- * 打印跨域对象
- * @param response
- * @param object
- */
- public static void printlnJSONPObject(HttpServletResponse response, Object object, String callback) {
- PrintWriter writer = getWriter(response);
- writer.println(callback + "(" + JSON.toJSONString(object) + ")");
- }
二前台 ajax 跨域请求数据
写法 1: 向服务器发送一个参数 callback=?, 同时指定 dataType 为'jsonp'的格式, 跨域请求时指定的数据格式必须是 jsonp 的形式
- function loadData(obj, level, id, value) {
- $.ajax({
- url: 'http://192.168.1.106:8086/carBrand/json.html?level=' + level + '&id=' + id + '&callback=?',
- // 将 callback 写在请求 url 后面作为参数携带
- type: 'GET',
- async: false,
- dataType: 'jsonp',
- success: function(data) {
- console.log(data);
- // 其他处理(动态添加数据元素)
- });
- }
写法 2:callback 不需要写在 url 中, 但是要指定 jsonp 参数为'callback', 并给 jsonpCallback 参数一个值
- function loadData(obj,level,id,value){
- $.ajax({
- url:'http://192.168.1.106:8086/carBrand/json.html?level='+level+'&id='+id,
- type:'GET',
- dataType:'jsonp',
- jsonp: 'callback', // 将 callback 写在 jsonp 里作为参数连同请求一起发送
- jsonpCallback:'jsonpCallback1',
- success:function(data){
- console.log(data);
- }); }
以上两种写法的含义是一样的, 只是写法不同罢了
接下来补充一下 jsonp 的工作原理
三 jsonp 跨域的原理解析
jsonp 的最基本的原理是: 动态添加一个 < script > 标签, 而 script 标签的 src 属性是没有跨域的限制的这样说来, 这种跨域方式其实与 ajax XmlHttpRequest 协议无关了.
JSONP 是一个非官方的协议, 它允许在服务器端集成 Script tags 返回至客户端, 通过 javascript callback 的形式实现跨域访问 JSONP 即 JSON with Padding 由于同源策略的限制, XmlHttpRequest 只允许请求当前源 (域名协议端口) 的资源如果要进行跨域请求, 我们可以通过使用 html 的 script 标记来进行跨域请求, 并在响应中返回要执行的 script 代码, 其中可以直接使用 JSON 传递 javascript 对象这种跨域的通讯方式称为 JSONP
jsonCallback 函数 jsonp1236827957501(....): 是浏览器客户端注册的, 获取跨域服务器上的 json 数据后, 回调的函数
Jsonp 原理:
首先在客户端注册一个 callback (如:'jsoncallback'), 然后把 callback 的名字 (如: jsonp1236827957501) 传给服务器注意: 服务端得到 callback 的数值后, 要用 jsonp1236827957501(......)把将要输出的 json 内容包括起来, 此时, 服务器生成 json 数据才能被客户端正确接收
然后以 javascript 语法的方式, 生成一个 function , function 名字就是传递上来的参数'jsoncallback'的值 jsonp1236827957501 .
最后将 json 数据直接以入参的方式, 放置到 function 中, 这样就生成了一段 js 语法的文档, 返回给客户端
客户端浏览器, 解析 script 标签, 并执行返回的 javascript 文档, 此时 javascript 文档数据, 作为参数,
传入到了客户端预先定义好的 callback 函数 (如上例中 jquery $.ajax() 方法封装的的 success: function (json))里.(动态执行回调函数)
可以说 jsonp 的方式原理上和 < script src="http:// 跨域 /...xx.js"></script > 是一致的 (qq 空间就是大量采用这种方式来实现跨域数据交换的) .JSONP 是一种脚本注入(Script Injection) 行为, 所以也有一定的安全隐患.
注意, jquey 是不支持 post 方式跨域的.
来源: http://www.jb51.net/article/134727.htm