摘要:其實很簡單的處理方式,只不夠優雅,或者說沒有找到fastjson爲其提供便捷的處理方式。所以記錄下處理該問題的過程。java
目標:將全部api請求,即響應爲APPLICATION_JSON的內容作統一格式處理。 例如:@RestController 標註類方法放回值爲List、 Map 或PO 類 增長響應字段 status。spring
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; }