扩展springMVC消息转换器来支持jsonp数据格式

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;
	}

}

然后在springMVC的配置文件中进行配置
	<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>

然后controller还是可以按照原来的写法,但此时已经默认支持jsonp
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


原文地址:https://www.cnblogs.com/chenny3/p/10226184.html