• 使用fastjson统一序列化响应格式


    摘要:其实很简单的处理方式,只不够优雅,或者说没有找到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;
        }
    
     
  • 相关阅读:
    hdu 1686 Oulipo
    [NOI1997] 积木游戏
    错误录——未完待续
    NOI 2014 魔法森林
    hdu 4010 Query on The Trees
    求助大佬6——1种贪心
    51 nod 1205 流水线调度
    bzoj 1180: [CROATIAN2009]OTOCI
    HNOI2010 弹飞绵羊
    SDOI2008 洞穴勘测
  • 原文地址:https://www.cnblogs.com/exmyth/p/11051184.html
Copyright © 2020-2023  润新知