淘淘商城系列——jsonp的原理及两种实现方式

来源:互联网 发布:win2008端口转发 编辑:程序博客网 时间:2024/06/05 01:08

首先,说下什么是跨域?有以下两种方式的跨域:

  1. 域名不同
  2. 域名相同,端口不同

解决js的跨域问题可以使用jsonp。那接下来说下什么是jsonp?
jsonp不是新技术,它只是一种跨域的解决方案,使用js的特性绕过跨域请求,利用的特性便是js可以跨域加载js文件!!举个非常常见的例子,我们在html头部一般都会引入很多js,甚至我们直接引用在线的js,比如我们引用官方网站的jQuery路径加载进来也是可以的。jQuery的官方域名与我们的工程所在的域名肯定是不一样的,但是不影响使用,这就是我们所说的js可以跨域请求js文件!
跨域的特点是什么呢?我们可以在浏览器的”Network”一栏看到请求的token信息,在Headers当中发现服务端已经正常响应了,如下图所示。
这里写图片描述
我们再看下Response一栏,发现根据token查询到了用户信息,但是就是没有把用户名展示到首页。其实流程是这样的,我们使用ajax调用服务端的接口,服务端不管你请求是否是同一个域下,只管响应,于是我们可以看到服务端响应的头信息,但是当数据经过浏览器时,浏览器判断出js访问的数据来自不同的域,于是便拒绝将数据返给页面,即使我们拿到了Response信息,也无法展示内容。
这里写图片描述
那么jsonp是怎样解决跨域问题的呢?流程图如下,图的上半部分展示的是传统跨域请求,这种请求我们是获取不到Response数据的,下半部分是介绍jsonp跨域获取数据的方案的。我们在要跨域获取信息的js当中添加一个函数,该函数有返回值参数data,发起跨域请求的一端引入服务端定义好的一个js文件,请求的参数中是带callback回调函数的,并且callback=mycall中的”mycall”一定是与我们添加的那个函数的名称一致的,服务端接收到请求,就进行响应,并且判断参数中是否有callback参数,如果有callback参数的话,就对要返回的数据进行处理,加工成一段js代码(很简单,就是把一个json串包装成一个js函数,如下图mycall({id:1,name:z});)然后响应,jsonp本来就是要请求访问服务端的一个js文件,现在返回的也是js代码,于是浏览器将不再进行拦截,由于返回的是js代码,而且js语句有个特点就是,一旦响应到浏览器端便立即执行,我们已经添加了该函数,于是便调用我们添加的那个函数,我们只需把数据做下处理并进行展示就可以了!!!
这里写图片描述
下面我们便来实现jsonp请求,上图所介绍的方法是比较复杂的,如果我们使用jQuery的话,jsonp解决跨域问题的流程如下图所示。
这里写图片描述
这样就会帮我们省去很多事,我们只需要在ajax请求时指定dataType为jsonp,如下图所示,jQuery便会帮我们自动创建一个函数。
这里写图片描述
我们刷新下淘淘商城首页,可以看到请求中自动为我们加上了回调函数,而且随机为我们添加了函数名为”jQuery1764509”的函数。
这里写图片描述
现在我们要做的便是修改服务端的代码,以便能够配合客户端完成整个请求过程,修改的方法如下图所示,我们将原来返回一个TaotaoResult类型改为返回String类型,而且添加了callback参数,判断下是否有callback参数,如果有就说明是jsonp请求,我们要将result转换成json串并且包装成一个js函数返回。如果不是jsonp请求,那就直接将result转换成json串返回。
这里写图片描述
getUserByToken方法修改后的代码如下所示:

// 传统支持jsonp的方案@RequestMapping(value="/user/token/{token}", produces=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic String getUserByToken(@PathVariable String token, String callback) {    TaotaoResult result = userLoginService.getUserByToken(token);    if (StringUtils.isNotBlank(callback)) {        // 客户端为jsonp请求,需要返回js代码        String jsonResult = callback + "(" + JsonUtils.objectToJson(result) + ");";        return jsonResult; // 统一返回字符串    }    return JsonUtils.objectToJson(result); // 统一返回字符串}

好了,代码修改完了,我们现在重启taotao-sso-web工程,重启后,我们刷新淘淘商城首页,发现页面正常显示用户的姓名了!!注意,如果你这时还显示不出来,是因为token已经过期了,你需要重新登录一下,这样就可以看到了。
这里写图片描述
上面介绍的是最通用的一种方法,还有一种方法也可以解决,只是要求spring的版本是4.1以上,还好,我们的淘淘商城用的版本是4.2,因此这是没问题的,要修改的还是UserController类的getUserByToken方法,如下图所示。
这里写图片描述
getUserByToken方法修改后的代码如下所示,由于result和mappingJacksonValue属于不同类型,因此返回值类型修改成了Object。

// 方法二,从4.1以上版本才可以使用@RequestMapping(value="/user/token/{token}")@ResponseBodypublic Object getUserByToken(@PathVariable String token, String callback) {    TaotaoResult result = userLoginService.getUserByToken(token);    if (StringUtils.isNotBlank(callback)) {        // 设置要包装的数据        MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);        // 设置回调方法        mappingJacksonValue.setJsonpFunction(callback);        return mappingJacksonValue;    }    return result;}

我们重启下taotao-sso-web工程,然后重新刷新下淘淘商城首页,如下图所示,发现也可以正常显示用户名!
这里写图片描述