關於使用fastjson統一序列化響應格式。

摘要:其實很簡單的處理方式,只不夠優雅,或者說沒有找到fastjson爲其提供便捷的處理方式。所以記錄下處理該問題的過程。java

目標:將全部api請求,即響應爲APPLICATION_JSON的內容作統一格式處理。 例如:@RestController 標註類方法放回值爲List、 Map 或PO 類 增長響應字段 status。spring

  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化。sql

@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註冊專用的內容攔截apache

@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等json

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.mapapi

@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.javabeanapp

@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 進行代理。ide

注意:不要單獨使用Enhancer代理,在com.alibaba.fastjson.serializer.SerializeConfig中:會對代理類進行解析。ui

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;
    }
相關文章
相關標籤/搜索