摘要:其实很简单的处理方式,只不够优雅,或者说没有找到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化。

  1. @Component
  2. public class GlobalExceptionResolver implements HandlerExceptionResolver {
  3. @Override
  4. public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
  5. Exception ex) {
  6. response.setStatus(getStatusCodeValue());
  7. response.setContentType(ContentType.APPLICATION_JSON.toString());
  8. response.setCharacterEncoding(Charsets.UTF_8.toString());
  9. ...
  10. }

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

  1. @ControllerAdvice
  2. public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
  3. private static Class<?>[] types = { Iterable.class, Iterator.class };
  4. @Override
  5. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  6. Class<?> clz = returnType.getMethod().getReturnType();
  7. for (Class<?> c : types) {
  8. if (c.isAssignableFrom(clz)) {
  9. return true;
  10. }
  11. }
  12. return clz.isArray();
  13. }
  14. @Override
  15. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  16. Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  17. ServerHttpResponse response) {
  18. Pages<?> p = Pages.builder(body);
  19. if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
  20. if (!Stream.of(returnType.getMethod().getAnnotations())
  21. .anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
  22. response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
  23. return JSONObject.toJSONString(p);
  24. }
  25. return p;
  26. }

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

  1. private static Class<?>[] types = { Clob.class, java.lang.CharSequence.class, java.lang.Number.class,
  2. LocalDate.class, LocalDateTime.class, LocalTime.class, java.util.Date.class, java.sql.Blob.class,
  3. java.sql.Array.class, };
  4. @Override
  5. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  6. Class<?> clz = returnType.getMethod().getReturnType();
  7. for (Class<?> t : types) {
  8. if (t.isAssignableFrom(clz)) {
  9. return true;
  10. }
  11. }
  12. return false;
  13. }
  14. @Override
  15. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  16. Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  17. ServerHttpResponse response) {
  18. JSONObject js = new JSONObject();
  19. js.put(AppEnum.status.getValue(), true);
  20. js.put(AppEnum.message.getValue(), body);
  21. if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
  22. if (!Stream.of(returnType.getMethod().getAnnotations())
  23. .anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
  24. response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
  25. return js.toString();
  26. }
  27. return js;
  28. }

4.map

  1. @SuppressWarnings({ "serial", "unchecked", "rawtypes" })
  2. private static final Map<Class<?>, Function<Object, Object>> types = new LinkedHashMap<Class<?>, Function<Object, Object>>() {
  3. {
  4. put(Map.class, obj -> {
  5. ((Map) obj).putIfAbsent(AppEnum.status.getValue(), true);
  6. return obj;
  7. });
  8. put(org.json.JSONObject.class, obj -> {
  9. JSONObject js = (JSONObject) obj;
  10. if (!js.containsKey(AppEnum.status.getValue()))
  11. js.put(AppEnum.status.getValue(), true);
  12. return js;
  13. });
  14. }
  15. };
  16. @Override
  17. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  18. Class<?> clz = returnType.getMethod().getReturnType();
  19. if (types.containsKey(clz))
  20. return true;
  21. for (Class<?> c : types.keySet()) {
  22. if (c.isAssignableFrom(clz)) {
  23. return true;
  24. }
  25. }
  26. return false;
  27. }
  28. @Override
  29. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  30. Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  31. ServerHttpResponse response) {
  32. body = types.get(returnType.getMethod().getReturnType()).apply(body);
  33. if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
  34. if (!Stream.of(returnType.getMethod().getAnnotations())
  35. .anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
  36. response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
  37. return JSONObject.toJSONString(body);
  38. }
  39. return body;
  40. }

5.javabean

  1. @Override
  2. public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
  3. return ParserConfig.global.getDeserializer(returnType.getMethod().getReturnType()) instanceof JavaBeanDeserializer;
  4. }
  5. @Override
  6. public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
  7. Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
  8. ServerHttpResponse response) {
  9. BeanGenerator bg = new BeanGenerator();
  10. bg.setSuperclass(body.getClass());
  11. bg.addProperty("status", Boolean.class);
  12. Object obj = bg.create();
  13. BeanUtil.beanCopier(obj, body);
  14. try {
  15. BeanUtils.setProperty(obj, "status", true);
  16. } catch (IllegalAccessException e) {
  17. e.printStackTrace();
  18. } catch (InvocationTargetException e) {
  19. e.printStackTrace();
  20. }
  21. if (MediaType.TEXT_HTML.equals(selectedContentType) || MediaType.TEXT_PLAIN.equals(selectedContentType)) {
  22. if (!Stream.of(returnType.getMethod().getAnnotations())
  23. .anyMatch(a -> a instanceof RequestMapping && !Objs.isEmpty(((RequestMapping) a).produces())))
  24. response.getHeaders().setContentType(MediaType.parseMediaType(MediaType.APPLICATION_JSON_UTF8_VALUE));
  25. return JSONObject.toJSONString(obj);
  26. }
  27. return obj;
  28. }

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

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

  1. private ObjectSerializer getObjectWriter(Class<?> clazz, boolean create) {
  2. if (TypeUtils.isProxy(clazz)) {
  3. Class<?> superClazz = clazz.getSuperclass();
  4. ObjectSerializer superWriter = getObjectWriter(superClazz);
  5. put(clazz, superWriter);
  6. return superWriter;
  7. }
  8. if (Proxy.isProxyClass(clazz)) {
  9. Class handlerClass = null;
  10. if (interfaces.length == 2) {
  11. handlerClass = interfaces[1];
  12. } else {
  13. for (Class proxiedInterface : interfaces) {
  14. if (proxiedInterface.getName().startsWith("org.springframework.aop.")) {
  15. continue;
  16. }
  17. if (handlerClass != null) {
  18. handlerClass = null; // multi-matched
  19. break;
  20. }
  21. handlerClass = proxiedInterface;
  22. }
  23. }
  24. if (handlerClass != null) {
  25. ObjectSerializer superWriter = getObjectWriter(handlerClass);
  26. put(clazz, superWriter);
  27. return superWriter;
  28. }
  29. }
  30. //-------------
  31. //TypeUtils
  32. public static boolean isProxy(Class<?> clazz){
  33. for(Class<?> item : clazz.getInterfaces()){
  34. String interfaceName = item.getName();
  35. if(interfaceName.equals("net.sf.cglib.proxy.Factory") //
  36. || interfaceName.equals("org.springframework.cglib.proxy.Factory")){
  37. return true;
  38. }
  39. if(interfaceName.equals("javassist.util.proxy.ProxyObject") //
  40. || interfaceName.equals("org.apache.ibatis.javassist.util.proxy.ProxyObject")
  41. ){
  42. return true;
  43. }
  44. }
  45. return false;
  46. }
 

使用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. darknet标签转化为COCO标签

    import sys import json import cv2 import os import shutil dataset = { "info": { "desc ...

  2. Python函数或者类的时间参数的默认值设为datetime.date.today()引起的问题

    定义了函数def main(start_date=datetime.date.today(), end_date=datetime.date.today()): pass 函数在项目启动后, end_ ...

  3. 学习笔记:oracle之win10安装卸载oracle 11gR2步骤及常见问题解决

    1.win10下安装oracle11g 1.1 工具原料 oracle11g安装包(64位) 1.2 步骤方法 1.在Oracle官网下载安装包,下载后,得到的文件如图所示: 2.将两个文件进行解压缩 ...

  4. Chrome 浏览器光标定位到地址栏

    Windows: Ctrl + L 或 Alt + D Mac: Command + L Linux: Ctrl + L

  5. S3. Android 消息推送

    [概要] 消息推送

  6. Sql 第一行某列减第二行某列

    --1. 将结果插入临时表SELECT *INTO xxx FROM( SELECT TOP 1 a.FQTY,a.fseq FROM T_SAL_ORDERENTRY as a WHERE FQTY ...

  7. XDomainRequest IE8&amp;IE9 cors 跨域通讯的处理方法

       版权声明:避免百度一下通片同一篇文章,未经博主允许不得转载.本博客作为笔记使用,正确性请自行验证. https://blog.csdn.net/u014071104/article/detail ...

  8. python第一个浏览器的自动执行程序

    1.目标:简单点,百度搜索“美丽的程序员” 2.操作方法: a.python已经安装完成 b.安装PIP:在windows的cmd窗口下输入easy_install pip      c.安装sele ...

  9. Linux (x86) Exploit 开发系列教程之七 绕过 ASLR -- 第二部分

    (1)原理: 使用爆破技巧,来绕过共享库地址随机化.爆破:攻击者选择特定的 Libc 基址,并持续攻击程序直到成功.这个技巧是用于绕过 ASLR 的最简单的技巧. (2)漏洞代码 //vuln.c # ...

  10. react封装通用tab组件

    import React, { Component } from 'react' import PropTypes from 'prop-types' import _ from 'lodash' i ...