用一个切面来统一返回前端的JSON格式
Posted 百里马
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用一个切面来统一返回前端的JSON格式相关的知识,希望对你有一定的参考价值。
新项目使用Spring MVC
+ MyBatis
架构来做,这套框架自己应该比较得心应手,这里来写一下这两天做的一些设计。
首先是静态资源的处理,关于这个之前有写文章单独讲过,这里不再重复写,不太清楚的童鞋可以移步查看
接着需要统一JSON
的返回格式,和前端工程师约定,对于字符串类型和日期类型都返回字符串,而对于普通数字类型的话都返回数字,金额类数字都返回格式化好的保留一位小数的字符串(比如”10.0”),另外数字和金额的默认值为0,而字符串的默认值为字符串空(“”),并且对于返回的格式也有规范,所有的JSON
返回必须形如:
"status": 1,
"data": data,
"msg": ""
其中:
status
代表了该请求的成功与否,若为1表示成功,若为0表示失败,此时msg
中将包含显示给用户的错误信息data
代表了返回给前端的数据,可以是一个复杂的数据结构msg
前面提到了,一般放异常信息。
下面,我们首先需要对默认的Jackson
的序列化规则做出修改,这里自定义一个ObjectMapper
:
/**
* 自定义jackson解析器
* @author Zhu
* @date 2015-5-14
* @version 0.0.1
* @description
*/
@Component
public class CustomerObjectMapper extends ObjectMapper
/**
*
*/
private static final long serialVersionUID = 1L;
/**
*
*/
public CustomerObjectMapper()
super();
// this.setSerializationInclusion(Include.NON_NULL);
// 允许单引号
// this.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 字段和值都加引号
// this.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 数字也加引号
// this.configure(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS, true);
// this.configure(JsonGenerator.Feature.QUOTE_NON_NUMERIC_NUMBERS, true);
this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>()
@Override
public void serialize(Object value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException
generator.writeString("");
);
然后将我们自定义的ObjectMapper
注入到MappingJackson2HttpMessageConverter
中去并重新配置Spring MVC
中的message converter
<bean id="mappingJackson2HttpMessageConverter"
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="customerObjectMapper"></property>
</bean>
<mvc:annotation-driven>
<mvc:message-converters>
<ref bean="mappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
接下来,我们可以在Controller
方法上添加@ResponseBody
注解来返回JSON
(这里需要说一句,classpath
下需要有jackson
的jar包,这是前提),但是这就要求我们在每个Controller
方法中都进行一些重复的工作,比如:
public ModelMap getUserList()
ModelMap modelMap = new ModelMap();
try
modelMap.put("status", 1);
modelMap.put("data", userSerivce.getUserList());
modelMap.put("", "");
catch (Exception e)
modelMap.put("status", 0);
modelMap.put("data", "");
modelMap.put("msg", e.getMessage());
logger.error("getUserList occurs error:", e);
这样的重复代码在项目中不建议出现,我们其实只需要关心其中的data就可以了,所以我打算将其统一写到一个方法中,并且这也有利于以后对于公共结构的更改。下面就是一个切面来统一处理所有的接口:
/**
* 其中ResponseBase类和注解类CustomResponseBody都是自己写的
*/
/**
* @author Zhu
* @date 2015-5-18
* @version 0.0.1
* @description 负责将返回转换成统一消息格式
* 序列化为的格式如下:
*
"status": 1,
"data": data,
"msg": ""
其中被注解的方法只需要关心data的内容即可
*/
@Aspect
@Component
public class ResponseAspect
@Resource
private MappingJackson2HttpMessageConverter converter;
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 拦截所有@ResponseBody
* @author Zhu
* @date 2015-5-18上午11:32:26
* @description
*/
@Pointcut("execution(* com.xxx.*.web.controller.*.*(..)) && @annotation(com.xxx.common.annotation.CustomResponseBody)")
public void responseBodyPointCut()
/**
* @author Zhu
* @date 2015-5-18上午11:35:42
* @description
* @param pjp
* @throws Throwable
*/
@Around(value = "responseBodyPointCut()")
@ResponseBody
public void formatResult2JSON(ProceedingJoinPoint pjp) throws Throwable
Object ret = pjp.proceed();
ResponseBase responseBase = new ResponseBase();
responseBase.setData(ret);
HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
converter.write(responseBase, MediaType.APPLICATION_JSON, outputMessage);
shutdownResponse(response);
/**
*
* @author Zhu
* @date 2015-5-18下午6:01:46
* @description
* @param jp
* @param error
* @throws Throwable
*/
@AfterThrowing(pointcut = "responseBodyPointCut()", throwing = "error")
public void handleForException(JoinPoint jp, Throwable error) throws Throwable
ResponseBase responseBase = new ResponseBase();
responseBase.setStatus(0);
responseBase.setMsg(error.getMessage());
HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse();
logger.error(jp.getSignature().getName() + "-error!", error);
HttpOutputMessage outputMessage = new ServletServerHttpResponse(response);
converter.write(responseBase, MediaType.APPLICATION_JSON, outputMessage);
shutdownResponse(response);
private void shutdownResponse(HttpServletResponse response) throws IOException
response.getOutputStream().close();
当然这里有几个关键点,需要提一下:
- 这也就提到了一个如何在
Spring
中手动使用配置好的Jackson
来序列化输出,这里我注入MappingJackson2HttpMessageConverter
,因为这里想要统一项目的JSON
序列化都交由Jackson
来做 - 由于是对
Controller
的代理,而一般Controller
是不实现接口的,那么就无法使用JDK
自带的动态代理,需要用到cglib
来做 - 关于上下文的问题,这里我在做的时候,由于碰到了两个上下文,即一般所说的rootContext和webContext。。。。
- 在自己返回json的情况下,需要shutdownResponse,即将Response关掉,不然可能会在这里输出json后还会再次输出一些内容
其余的内容后续再写吧,现在手头上项目太多,需要整理的东西也太多了~
以上是关于用一个切面来统一返回前端的JSON格式的主要内容,如果未能解决你的问题,请参考以下文章