跨域问题
提到jsonp首先就要提一下跨域。因为只有遇到跨域问题才会使用jsonp。
现在的项目一般都是分布式项目,既然是分布式项目肯定就会涉及到跨域请求资源的问题,那么什么是跨域。
我们对资源的访问都是通过url,即域名+各种 参数。
当域名,端口号,协议不同时,即为跨域。
这种情况下就要使用jsonp来进行跨域请求。
jsonp的原理是利用src能跨域请求资源的特性来实现的,细心的人能够发现,我们在用src引入网络上的而非本地文件的jquery.js时能够正常引入和使用,而网络上的jquery资源域名,端口号,协议和我们的项目都不同,也就是跨域,但是能正常使用,也就证明了src可以跨域请求资源。
ajax使用jsonp标识了dataType后就是告诉jquery要使用跨域请了,赶紧准备好callback函数,即在Ajax请求的地址后面加上:?callback=xxx,
例如:http://localhost:8080/xxx/xx?callback=xxx
那么后台在拿到请求后需要根据请求参数中是否有callback参数,来判断返回值是否要包裹callback函数。
如何使用Jsonp?
jquery的ajax可以很方便的使用jsonp,只需要在dataType上标识为jsonp即可。
$.ajax({
type : "post",
url: "http://localhost:8080/xxx/xxx/xxx",
data:data,
dataType : "jsonp",//数据类型为jsonp
jsonpCallback:"callback",
success : function(data){
},
error:function(){
}
});
后台controller
@RequestMapping(value="getResult",method=RequestMethod.GET)
public ResponseEntity<String> getResult(int limit,int offset,String search,HttpServletRequest request){
try {
EasyUIResult result=userService.queryAllUsers(limit,offset+1,search);
String callback=request.getParameter("callback");
if("".equals(callback)){
return ResponseEntity.status(HttpStatus.OK).body(result.toString());
}else{
//包裹callback函数,给js解析。
return ResponseEntity.ok(callback+"("+result+")");
}
} catch (Exception e) {
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
但是我们发现如果一个项目中跨域请求太多,那么后端需要在controller层中给每个jsonp请求进行callback函数包裹。显然太麻烦。我们可以给spring扩展一下,使其统一支持jsonp。
编写一个类继承自spring的MappingJackson2HttpMessageConverter类,
package cn.itcast.usermanage.util;
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 CallBackMappingJackson2HttpMessageConvert extends MappingJackson2HttpMessageConverter {
//做jsonP统一支持标识
private String callbackName;
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
String callbackParam=request.getParameter(callbackName);
if(StringUtils.isEmpty(callbackParam)){
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);
}
}
super.writeInternal(object, outputMessage);
}
}
使用该扩展类,在springmvc.xml里配置刚刚写的消息转化器。
<!-- 注解驱动,升级版的默认配置,不配置此项,springmvc也会自动加载相关默认配置,但是都是废弃的不建议使用的 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="cn.itcast.usermanage.util.CallBackMappingJackson2HttpMessageConvert">
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
然后controller层就可以修改成原来返回json的形式,不需要在controller里判断是否请求里是否含有callback参数,因为配置完成后spring会自动处理。