摘要:其实很简单的处理方式,只不够优雅,或者说没有找到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. 报错: Domain=NSCocoaErrorDomain Code=3000 "未找到应用程序的“aps-environment”的权利字符串"

    Code=3000 "未找到应用程序的“aps-environment”的权利字符串" 解决办法 push,即远程通知,教材的资料较少,想实践的话,只能每年花99美元买苹果开发者帐 ...

  2. Mac下用apache搭建一个局域网服务器

    一:由于MacOX系统下自带Apache环境,所以我们在Mac系统下用Apache配置. Mac系统:10.14.4 二:启动Apache 启动 在终端输入:sudo apachectl start验 ...

  3. python3.7.3安装beautifulsoup4出现版本不兼容的问题

    今天想安装一个beautifulsoup4,结果一直出错,好多教程总是有各种坑……找了很多个教程,为了记录方法也为了分享给大家,简单些一个.但是是真真不想再费劲写一遍了……直接贴链接,亲测有效,但是底 ...

  4. 配置node低版本支持ES7

    由于node的版本为6.*,不升级情况下,能使用ES7的 async await 特性, 在目录下 添加 start.js package.json里添加依赖配置 重新npm install 下载依赖 ...

  5. markdown转移字符表

    本片转的ASCII码,主要针对$,另外我为了不让"&#xxx;"被转移成字符,我在分号";"前加了个空格,复制的时候注意一下 字符 转义 0 空格 @ ...

  6. springboot集成elk 二:springboot + elk 采集日志

    到logstash-2.0.0\bin下新建文件 logstash.conf input { tcp { mode => "server" host => " ...

  7. oracle学习笔记day1

    oracle数据库前言 oracle sqllesson1 Selecting Rowslesson2 Sorting & Limiting Selected Rowslesson3 Sing ...

  8. Java面试笔记整理4

    一.Java内存溢出的产生原因和解决办法? java.lang.OutOfMemoryError这个错误我相信大部分开发人员都有遇到过,产生该错误的原因大都出于以下原因:JVM内存过小.程序不严密,产 ...

  9. ConcurrentHashMap能完全替代HashTable吗?

    至此你应该能够明白,ConcurrentHashMap与HashTable都可以用于多线程的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间.因为C ...

  10. MongoDB进阶之路:不仅仅是技术研究,还有优化和最佳实践--转载

    摘要:MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. 本文将从操作手册.技术研究.会议分享.场景应用等几个方面给大家推荐干货好文 ...