zoukankan      html  css  js  c++  java
  • 扩展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


  • 相关阅读:
    【SpringBoot】02 概述
    【SpringBoot】01 快速上手
    【SpringMVC】12 文件上传和下载
    【SpringMVC】10 对Ajax的应用
    【SpringMVC】11 拦截器
    【SpringMVC】09 对JSON的应用
    【SpringMVC】08 Post请求乱码
    【SpringMVC】06 转发 & 重定向
    【Mybatis】Bonus02 补充
    【SpringMVC】05 RestFul风格
  • 原文地址:https://www.cnblogs.com/chenny3/p/10226184.html
Copyright © 2011-2022 走看看