1.JSONP是用来解决json跨域问题的技术。即将传输的json转化为js脚本。
Callback是JSONP的实现的一种方式,例:getData([{"id":1,"title":"XXXX"},{"id":2,"title":"YYYYY"}])。当浏览器读取到funName(json文本)就会用js的语法解析,然后调用定义好的js getData函数,以回调函数的形式获取json数据。
2.为什么要在spring mvc中对于jsonp要自己处理?
因为spring mvc默认是不支持jsonp的,所以需要我们自己处理
3.如何在服务器端实现对JSONP支持
根据前台请求url是否携带callback,来判断是否需要jsonp。
a)如果携带,说明出现跨域问题,服务器就需要对跨域问题进行处理,处理方式这里采用callback,方法执行完后服务器会返回指定callback名的js script。
b)如果没携带,说明没有跨域问题,服务器返回普通json
之前的解决方式:
在controller中自行处理,判断请求中是否携带callback回调函数名。如果没有,则不对json进行处理。这种情况多适用于同一个系统下的ajax请求,不出现ajax跨域问题情况下。如果有,就对json进行处理,使其符合jsonp规范。这种情况适用于不同系统的ajax请求,出现跨域问题的情况下。
package com.taotao.manager.controller.api;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.common.ItemCatResult;
import com.taotao.manager.service.ItemCatService;
@Controller
@RequestMapping("/api/item/cat")
public class ApiItemCatController {
@Resource
private ItemCatService itemCatService;
private ObjectMapper objectMapper=new ObjectMapper();
@RequestMapping(method=RequestMethod.GET)
/**
* required=false 当访问此方法没有携带对应callback的参数值,则属性值默认为null。如果不设置并且没有携带数据,会抛出异常
* @param callback 回调函数名
* @return 符合jsonp规范的json格式数据
*/
public ResponseEntity<String> all(@RequestParam(value="callback",required=false) String callback){
try {
ItemCatResult result = itemCatService.queryAllToTree();//从后台查询处数据
if(result!=null){
String resultJson = objectMapper.writeValueAsString(result);//jackson组件把后台数据转化为json
if(StringUtils.isEmpty(callback)){//前台是否传callback回调函数名
return ResponseEntity.ok(resultJson);//没有的话,返回普通的json
}else{
return ResponseEntity.ok(callback+"("+resultJson+")");//有,返回封装后的json
}
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
因为返回类型是ResponseEntity<String>,后台string对象转换为前台数据是在StringHttpMessageConverter消息转换器中转换的,它默认的编码是iso-8859-1,前台接收的数据如果产生乱码,我们需要在spring-mvc配置文件中写如下配置
<mvc:annotation-driven>
<!-- 自定义消息转换器 -->
<mvc:message-converters register-defaults="true">
<!-- 自定义消息转换器,设置编码为utf-8,防止responseEntity<String>转换成json对象输出乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg index="0" value="utf-8"></constructor-arg>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
这种方式实现比较麻烦。因为在每个需要json跨域问题的controller中都要自己手动写代码判断处理
通过扩展springMVC消息转换器使得springMVC默认支持jsonp
首先写一个类继承org.springframework.http.converter.json.MappingJackson2HttpMessageConverter类,并重写writeInternal()方法
package com.taotao.common.spring.exetend.converter.json;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonProcessingException;
public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
// 做jsonp的支持的标识,在请求参数中加该参数
private String callbackName;
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 从threadLocal中获取当前的Request对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String callbackParam = request.getParameter(callbackName);
if(StringUtils.isEmpty(callbackParam)){
// 没有找到callback参数,直接返回json数据
super.writeInternal(object, outputMessage);
}else{
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
try {
String result =callbackParam+"("+super.getObjectMapper().writeValueAsString(object)+");";
IOUtils.write(result, outputMessage.getBody(),encoding.getJavaName());
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
}
public String getCallbackName() {
return callbackName;
}
public void setCallbackName(String callbackName) {
this.callbackName = callbackName;
}
}
<mvc:annotation-driven>
<!-- 自定义消息转换器 -->
<mvc:message-converters>
<!-- 自定义支持jsonp的MappingJackson2HttpMessageConverter消息转发器 -->
<bean class="com.taotao.common.spring.exetend.converter.json.CallbackMappingJackson2HttpMessageConverter">
<!-- 做jsonp的支持的标识,回调函数名,在请求参数中加该参数 -->
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
package com.taotao.manager.controller.api;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.common.ItemCatResult;
import com.taotao.manager.service.ItemCatService;
@Controller
@RequestMapping("/api/item/cat")
public class ApiItemCatController {
@Resource
private ItemCatService itemCatService;
private ObjectMapper objectMapper=new ObjectMapper();
@RequestMapping(method=RequestMethod.GET)
/**
* required=false 当访问此方法没有携带对应callback的参数值,则属性值默认为null。如果不设置并且没有携带数据,会抛出异常
* @param callback 回调函数名
* @return 符合jsonp规范的json格式数据
*/
public ResponseEntity<ItemCatResult> all(@RequestParam(value="callback",required=false) String callback){
try {
ItemCatResult result = itemCatService.queryAllToTree();
if(result!=null){
return ResponseEntity.ok(result);
}
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
测试:
普通的获取json方式:http://manage.taotao.com/rest/api/item/cat
解决跨域请求问题下,请求json数据的url:http://manage.taotao.com/rest/api/item/cat?callback=category.getDataService