使用fastjson统一序列化响应格式
摘要:其实很简单的处理方式,只不够优雅,或者说没有找到fastjson为其提供便捷的处理方式。因此记录下处理该问题的过程。
目标:将所有api请求,即响应为APPLICATION_JSON的内容做统一格式处理。 例如:@RestController 标注类方法放回值为List、 Map 或PO 类 增加响应字段 status。
- 当成功响应List 时,返回 {"data":[],"status":true}
- 当成功响应非List时,
- String : {"msg":"str","status":true}
- Map: {"k1":"v1","status":true}
- PO: {"column1":"v1","status":true}
- 当业务不能如预期处理,即异常响应:{"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统一序列化响应格式的更多相关文章
- ASP.NET Web API 2.0 统一响应格式
传统实现 在搭建 Web API 服务的时候,针对客户端请求,我们一般都会自定义响应的 JSON 格式,比如: { "Data" : { "Id" : 100, ...
- spring boot / cloud (二) 规范响应格式以及统一异常处理
spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...
- WebAPI接口设计:SwaggerUI文档 / 统一响应格式 / 统一异常处理 / 统一权限验证
为什么还要写这类文章?因为我看过网上很多讲解的都不够全面,而本文结合实际工作讲解了swaggerui文档,统一响应格式,异常处理,权限验证等常用模块,并提供一套完善的案例源代码,在实际工作中可直接参考 ...
- SpringBoot06 统一响应格式
1 要求 每个请求成功后,后台返回的响应格式都是一致的,例如: 2 创建一个视图模型 该模型用于格式化响应数据 package cn.xiangxu.springboottest.model.data ...
- spring boot:使接口返回统一的RESTful格式数据(spring boot 2.3.1)
一,为什么要使用REST? 1,什么是REST? REST是软件架构的规范体系,它把资源的状态用URL进行资源定位, 以HTTP动作(GET/POST/DELETE/PUT)描述操作 2,REST的优 ...
- 001-RESTful服务最佳实践-RestFul准则、HTTP动词表示含义、合理的资源命名、响应格式XML和JSON
一.概述 因为REST是一种架构风格而不是严格的标准,所以它可以灵活地实现.由于这种灵活性和结构自由度,对设计最佳实践也有很大的差异. API的方向是从应用程序开发人员的角度考虑设计选择. 幂等性 不 ...
- SpringBoot Redis使用fastjson进行序列化
在使用spring-data-redis,默认情况下是使用org.springframework.data.redis.serializer.JdkSerializationRedisSerializ ...
- FastJson bean序列化属性顺序问题
fastjson序列化一个java bean,默认是根据fieldName的字母序进行序列化的,你可以通过ordinal指定字段的顺序,这个特性需要1.1.42以上版本.示例如下. import co ...
- fastjson自定义序列化竟然有这么多姿势?
本文介绍下fastjson自定义序列化的各种操作. 一.什么是fastjson? fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSO ...
随机推荐
- darknet标签转化为COCO标签
import sys import json import cv2 import os import shutil dataset = { "info": { "desc ...
- Python函数或者类的时间参数的默认值设为datetime.date.today()引起的问题
定义了函数def main(start_date=datetime.date.today(), end_date=datetime.date.today()): pass 函数在项目启动后, end_ ...
- 学习笔记:oracle之win10安装卸载oracle 11gR2步骤及常见问题解决
1.win10下安装oracle11g 1.1 工具原料 oracle11g安装包(64位) 1.2 步骤方法 1.在Oracle官网下载安装包,下载后,得到的文件如图所示: 2.将两个文件进行解压缩 ...
- Chrome 浏览器光标定位到地址栏
Windows: Ctrl + L 或 Alt + D Mac: Command + L Linux: Ctrl + L
- S3. Android 消息推送
[概要] 消息推送
- Sql 第一行某列减第二行某列
--1. 将结果插入临时表SELECT *INTO xxx FROM( SELECT TOP 1 a.FQTY,a.fseq FROM T_SAL_ORDERENTRY as a WHERE FQTY ...
- XDomainRequest IE8&IE9 cors 跨域通讯的处理方法
版权声明:避免百度一下通片同一篇文章,未经博主允许不得转载.本博客作为笔记使用,正确性请自行验证. https://blog.csdn.net/u014071104/article/detail ...
- python第一个浏览器的自动执行程序
1.目标:简单点,百度搜索“美丽的程序员” 2.操作方法: a.python已经安装完成 b.安装PIP:在windows的cmd窗口下输入easy_install pip c.安装sele ...
- Linux (x86) Exploit 开发系列教程之七 绕过 ASLR -- 第二部分
(1)原理: 使用爆破技巧,来绕过共享库地址随机化.爆破:攻击者选择特定的 Libc 基址,并持续攻击程序直到成功.这个技巧是用于绕过 ASLR 的最简单的技巧. (2)漏洞代码 //vuln.c # ...
- react封装通用tab组件
import React, { Component } from 'react' import PropTypes from 'prop-types' import _ from 'lodash' i ...