摘要:其实很简单的处理方式,只不够优雅,或者说没有找到fastjson为其提供便捷的处理方式。因此记录下处理该问题的过程。

目标:将所有api请求,即响应为APPLICATION_JSON的内容做统一格式处理。 例如:@RestController 标注类方法放回值为List、 Map 或PO 类 增加响应字段 status。

  1. 当成功响应List 时,返回 {"data":[],"status":true}
  2. 当成功响应非List时,
  • String : {"msg":"str","status":true}
  • Map: {"k1":"v1","status":true}
  • PO: {"column1":"v1","status":true}
  1. 当业务不能如预期处理,即异常响应:{"msg":"异常理由","status":false}

1:业务预期失败 在spring boot中配置HandlerExceptionResolver后,即可拦截处理所有异常,在此便可处理错误信息的json化。

@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver { @Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
response.setStatus(getStatusCodeValue());
response.setContentType(ContentType.APPLICATION_JSON.toString());
response.setCharacterEncoding(Charsets.UTF_8.toString());
...
}

**2.集合情况 ** 使用ControllerAdvice注册专用的内容拦截

@ControllerAdvice
public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
private static Class<?>[] types = { Iterable.class, Iterator.class }; @Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
Class<?> clz = returnType.getMethod().getReturnType();
for (Class<?> c : types) {
if (c.isAssignableFrom(clz)) {
return true;
}
}
return clz.isArray();
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
Pages<?> p = Pages.builder(body);
if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
if (!Stream.of(returnType.getMethod().getAnnotations())
.anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
return JSONObject.toJSONString(p);
}
return p;
}

3.基本类型,如String,Number等

	private static Class<?>[] types = { Clob.class, java.lang.CharSequence.class, java.lang.Number.class,
LocalDate.class, LocalDateTime.class, LocalTime.class, java.util.Date.class, java.sql.Blob.class,
java.sql.Array.class, }; @Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
Class<?> clz = returnType.getMethod().getReturnType();
for (Class<?> t : types) {
if (t.isAssignableFrom(clz)) {
return true;
}
}
return false;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
JSONObject js = new JSONObject();
js.put(AppEnum.status.getValue(), true);
js.put(AppEnum.message.getValue(), body);
if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
if (!Stream.of(returnType.getMethod().getAnnotations())
.anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
return js.toString();
}
return js;
}

4.map

	@SuppressWarnings({ "serial", "unchecked", "rawtypes" })
private static final Map<Class<?>, Function<Object, Object>> types = new LinkedHashMap<Class<?>, Function<Object, Object>>() {
{
put(Map.class, obj -> {
((Map) obj).putIfAbsent(AppEnum.status.getValue(), true);
return obj;
});
put(org.json.JSONObject.class, obj -> {
JSONObject js = (JSONObject) obj;
if (!js.containsKey(AppEnum.status.getValue()))
js.put(AppEnum.status.getValue(), true);
return js;
});
}
}; @Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
Class<?> clz = returnType.getMethod().getReturnType();
if (types.containsKey(clz))
return true;
for (Class<?> c : types.keySet()) {
if (c.isAssignableFrom(clz)) {
return true;
}
}
return false;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
body = types.get(returnType.getMethod().getReturnType()).apply(body);
if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
if (!Stream.of(returnType.getMethod().getAnnotations())
.anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
return JSONObject.toJSONString(body);
}
return body;
}

5.javabean

	@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return ParserConfig.global.getDeserializer(returnType.getMethod().getReturnType()) instanceof JavaBeanDeserializer;
} @Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
ServerHttpResponse response) {
BeanGenerator bg = new BeanGenerator();
bg.setSuperclass(body.getClass());
bg.addProperty("status", Boolean.class);
Object obj = bg.create();
BeanUtil.beanCopier(obj, body);
try {
BeanUtils.setProperty(obj, "status", true);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
if (!Stream.of(returnType.getMethod().getAnnotations())
.anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
return JSONObject.toJSONString(obj);
}
return obj;
}

对于javabean。之前采用的是fastjson的BeforeFilter,但有个问题,在JavaBeanSerializer会重复的对javaBean对象进行序列化,这会导致除了在最外层的javaBean 中添加status外,还会导致所有的javaBean都被添加。因此后续改成了通过net.sf.cglib.beans.BeanGenerator 对javaBean 进行代理。

注意:不要单独使用Enhancer代理,在com.alibaba.fastjson.serializer.SerializeConfig中:会对代理类进行解析。

	private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
if (TypeUtils.isProxy(clazz)) {
Class<?> superClazz = clazz.getSuperclass(); ObjectSerializer superWriter = getObjectWriter(superClazz);
put(clazz, superWriter);
return superWriter;
} if (Proxy.isProxyClass(clazz)) {
Class handlerClass = null; if (interfaces.length == 2) {
handlerClass = interfaces[1];
} else {
for (Class proxiedInterface : interfaces) {
if (proxiedInterface.getName().startsWith("org.springframework.aop.")) {
continue;
}
if (handlerClass != null) {
handlerClass = null; // multi-matched
break;
}
handlerClass = proxiedInterface;
}
} if (handlerClass != null) {
ObjectSerializer superWriter = getObjectWriter(handlerClass);
put(clazz, superWriter);
return superWriter;
}
} //-------------
//TypeUtils
public static boolean isProxy(Class<?> clazz){
for(Class<?> item : clazz.getInterfaces()){
String interfaceName = item.getName();
if(interfaceName.equals("net.sf.cglib.proxy.Factory") //
|| interfaceName.equals("org.springframework.cglib.proxy.Factory")){
return true;
}
if(interfaceName.equals("javassist.util.proxy.ProxyObject") //
|| interfaceName.equals("org.apache.ibatis.javassist.util.proxy.ProxyObject")
){
return true;
}
}
return false;
}
 

使用fastjson统一序列化响应格式的更多相关文章

  1. ASP.NET Web API 2.0 统一响应格式

    传统实现 在搭建 Web API 服务的时候,针对客户端请求,我们一般都会自定义响应的 JSON 格式,比如: { "Data" : { "Id" : 100, ...

  2. spring boot / cloud (二) 规范响应格式以及统一异常处理

    spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...

  3. WebAPI接口设计:SwaggerUI文档 / 统一响应格式 / 统一异常处理 / 统一权限验证

    为什么还要写这类文章?因为我看过网上很多讲解的都不够全面,而本文结合实际工作讲解了swaggerui文档,统一响应格式,异常处理,权限验证等常用模块,并提供一套完善的案例源代码,在实际工作中可直接参考 ...

  4. SpringBoot06 统一响应格式

    1 要求 每个请求成功后,后台返回的响应格式都是一致的,例如: 2 创建一个视图模型 该模型用于格式化响应数据 package cn.xiangxu.springboottest.model.data ...

  5. spring boot:使接口返回统一的RESTful格式数据(spring boot 2.3.1)

    一,为什么要使用REST? 1,什么是REST? REST是软件架构的规范体系,它把资源的状态用URL进行资源定位, 以HTTP动作(GET/POST/DELETE/PUT)描述操作 2,REST的优 ...

  6. 001-RESTful服务最佳实践-RestFul准则、HTTP动词表示含义、合理的资源命名、响应格式XML和JSON

    一.概述 因为REST是一种架构风格而不是严格的标准,所以它可以灵活地实现.由于这种灵活性和结构自由度,对设计最佳实践也有很大的差异. API的方向是从应用程序开发人员的角度考虑设计选择. 幂等性 不 ...

  7. SpringBoot Redis使用fastjson进行序列化

    在使用spring-data-redis,默认情况下是使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializ ...

  8. FastJson bean序列化属性顺序问题

    fastjson序列化一个java bean,默认是根据fieldName的字母序进行序列化的,你可以通过ordinal指定字段的顺序,这个特性需要1.1.42以上版本.示例如下. import co ...

  9. fastjson自定义序列化竟然有这么多姿势?

    本文介绍下fastjson自定义序列化的各种操作. 一.什么是fastjson? fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSO ...

随机推荐

  1. 【c# 学习笔记】数值类型

    c# 中的数值类型分为:整型.浮点型.十进制.如下图. 1.整型数值的取值范围和内存大小 类型 说明 取值范围 byte 无符号的8位整数 0到255(即2的8次方-1) sbyte   有符号的8位 ...

  2. 从源码角度解析Netty的React模式是如何工作的

    Netty 支持多种实现方式,比如nio,epoll 等,本文以nio的实现方式进行讲解. 1.EventLoop : 事件循环看,简单来说就是一个死循环监听事件,如果事件来了,处理掉.通常做法就是开 ...

  3. SVN安装使用【转】

    SVN使用教程总结   SVN简介: 为什么要使用SVN? 程序员在编写程序的过程中,每个程序员都会生成很多不同的版本,这就需要程序员有效的管理代码,在需要的时候可以迅速,准确取出相应的版本. Sub ...

  4. VS2010 如何在调试的时候输入参数

    VS2010 如何在调试的时候输入参数 声明:引用请注明出处http://blog.csdn.net/lg1259156776/ 很明显,好多算法程序实现的时候提供的例程都是需要在命令行中输入参数,比 ...

  5. 关于iframe的一些使用

    在iframe页面,获取当前iframe的id var iframeId = window.frameElement.id.toString(); 获取父窗口中另一个iframe的iframe,注意返 ...

  6. 乐字节Java学习03-path环境变量

    1. path环境变量的作用 保证javac命令可以在任意目录下运行. 2. path配置的两种方案: 方法 一如下: ①点击计算机->右键->属性 ②高级系统设置 ③高级—>环境变 ...

  7. [转帖]linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习)

    linux常用命令大全(linux基础命令入门到精通+实例讲解+持续更新+命令备忘录+面试复习) https://www.cnblogs.com/caozy/p/9261224.html 总结的挺好的 ...

  8. [转帖]可能是东半球最好的 Curl 学习指南,强烈建议收藏!

    可能是东半球最好的 Curl 学习指南,强烈建议收藏! http://www.itpub.net/2019/09/30/3302/ 记得转帖过.. 简介 curl 是常用的命令行工具,用来请求 Web ...

  9. [转帖]降低 80% 的读写响应延迟!我们测评了 etcd 3.4 新特性(内含读写发展史)

      降低 80% 的读写响应延迟!我们测评了 etcd 3.4 新特性(内含读写发展史) https://www.cnblogs.com/alisystemsoftware/p/11555426.ht ...

  10. K8S 从入门到放弃系列文章目录(Kubernetes 1.14)

    1)软件环境 软件 版本 系统 Centos7.5 Kubernetes 1.14.1 Docker 18.09 Calico 3.6 Etcd 3.3.12 2)部署过程简单概要 三台master节 ...