核心内容摘要
寻找“ww”式的微醺快乐:生活不在别处,就在每一个被治愈的瞬间
01 引言在程序编码的时候我们总会统一返回响应参数以规范开发。
但是面对老项目改造的时候有没有遇到过返回的参数不统一的情况自己又不原因一个个的纠正。
那么这篇文章将从不同的角度给你带来思路。
02 ResponseBodyAdvice类似一个切面在响应参数返回之前执行。
按照官方文档的释义就是在使用HttpMessageConverter之前执行ResponseBody或者ResponseEntity之后被触发。
可以直接向RequestMappingHandlerAdapter和ExceptionHandlerExceptionResolver注册也可以直接通过ControllerAdvice被触发。
1 场景假设假设我们系统中有以下三种返回方式GetMapping(/testResponseEntity) public ResponseEntityWjson testResponseEntity() { return ResponseEntity.ok(new Wjson(test_ResponseEntity, v
); } GetMapping(/testWjson) public Wjson testWjson() { return new Wjson(test_wjson, v
; } GetMapping(/testJsonResult) public JsonResultWjson testJsonResult() { return JsonResult.success(new Wjson(test_json, v
); }现在来了一个技术Leader想要重构现有的结构需要统一返回参数决定最终的返回结果如下{ code: 0000, data: { content: test_json, version: v3 }, success: true }并给出了统一返回参数的实体Data public class JsonResultT { private String code; private String msg; private T data; private boolean success; public static T JsonResultT success(T data) { JsonResult jr new JsonResult(); jr.setData(data); jr.setSuccess(true); jr.setCode(
; return jr; } public static T JsonResultT fail(String code, String msg) { JsonResult jr new JsonResult(); jr.setSuccess(false); jr.setCode(code); jr.setMsg(msg); return jr; } }
2 问题解决强迫症的解决方案就是一个个的修改每一个方法。
这种方案自然可行但是当方法很多时工作量就非常大显然不太适合。
于是就想从框架层面解决统一返回的问题。
ResponseBodyAdvice该上场了。
我们需要定于属于自己的请求体切面。
ControllerAdvice public class MyResponseBodyAdvice implements ResponseBodyAdviceObject { Override public boolean supports(MethodParameter returnType, Class? extends HttpMessageConverter? converterType) { return returnType.getContainingClass().isAnnotationPresent(RestController.class); } Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class? extends HttpMessageConverter? selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof JsonResult) { return body; } if (body instanceof Wjson) { return JsonResult.success(body); } if (body instanceof ResponseEntity) { return JsonResult.success(((ResponseEntity?) body).getBody()); } return JsonResult.fail(30002, 请求失败); } }supports什么样的条件进入允许处理类似开关。
案例中返回的方法上是否包含RestController.class。
beforeBodyWritesupports返回true之后进入。
案例中如果返回JsonResult不处理。
如果是Wjson或者ResponseEntity需要转化成JsonResult否则返回错误信息。
3 结果演示这样就不在修改Controller的情况下完成响应参数的统一封装。
03 番外原本上面的解决方案已经是常见的解决方案了。
但是前两天在整理SpringBoot配置项时发现HandlerMethodReturnValueHandler同样可以解决此类问题功能甚至更丰富。
1 自定统一返回处理public class JsonResultHandlerMethodReturnValueHandler implements HandlerMethodReturnValueHandler { Override public boolean supportsReturnType(MethodParameter returnType) { return returnType.getContainingClass().isAnnotationPresent(RestController.class); } Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 标记请求已处理避免视图解析 mavContainer.setRequestHandled(true); HttpServletResponse response webRequest.getNativeResponse(HttpServletResponse.class); response.setContentType(MediaType.APPLICATION_JSON_VALUE); response.setCharacterEncoding(UTF-
; PrintWriter writer response.getWriter(); if (returnValue instanceof JsonResult) { writer.write(JSON.toJSONString(returnValue)); return; } if (returnValue instanceof Wjson) { writer.write(JSON.toJSONString(JsonResult.success(returnValue))); return; } if (returnValue instanceof ResponseEntity) { writer.write(JSON.toJSONString(JsonResult.success(((ResponseEntity?) returnValue).getBody()))); return; } writer.write(JSON.toJSONString(JsonResult.fail(30001, 请求失败))); } }
2 配置为防止自定义的方法返回解析器被系统默认的解析器有限取代执行我们需要调整解析器的位置。
Autowired private RequestMappingHandlerAdapter handlerAdapter; PostConstruct public void init() { ListHandlerMethodReturnValueHandler returnValueHandlers handlerAdapter.getReturnValueHandlers(); ListHandlerMethodReturnValueHandler list new ArrayList(returnValueHandlers); list.add(0, new JsonResultHandlerMethodReturnValueHandler()); handlerAdapter.setReturnValueHandlers(list); }结果这里就不演示了和上面的结果一致。
04 小结条条道路通罗马每一种方式都能解决问题。
但是不同方案处理数据的时机不同HandlerMethodReturnValueHandler处理的方式更加灵活可以随意定制。
而ResponseBodyAdvice更像一个定制好的半成品使用起来更加简单。