API統一數據返回的JSON格式,可使用統一返回類,AOP,過濾器,攔截器或實現HandlerMethodReturnValueHandler方法。
AOP
利用環繞通知,對包含@RequestMapping註解的方法統一處理
優點:配置簡單、可捕獲功能方法內部的異常
缺點:aop不能修改返回結果的類型,因此功能方法的返回值須統一爲Object類型
過濾器
在過濾器層統一處理
優點:配置簡單
缺點:無法識別異常結果,須對返回結果進行額外的反序列化
攔截器
獲取返回值不方便,且無法獲取到String類型的返回值,無法實現該功能
HandlerMethodReturnValueHandler
無上述各方法的缺點,且能複用@ResponseBody等註解
SpringMVC流程
在SpringMVC流程中,在調用 HandlerAdapter 的 處理方法的時候 會跳轉調用到 RequestMappingHandlerAdapter 的 handleInternal 方法。這裏面會將 原本的處理器 HandlerMethod 封裝成 ServletInvocableHandlerMethod,然後會調用這個類中的 invokeAndHandle 方法,這個方法中主要進行了相應方法處理器的方法的調用,在調用完成後,會處理返回值。
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
在handleReturnValue方法中,會查找合適的處理器Processor,並通過supportsReturnType方法判斷是否支持。
若使用了@ResponseBody註解的接口,都會被RequestResponseBodyMethodProcessor處理。所以我們可以在該處理器處理之前,將返回值修改成統一的JSON格式。
具體實現
在初始化ResponseBodyWrapBean時,獲取所有的SpringMVC的內部HandlerMethodReturnValueHandler實現,並將RequestResponseBodyMethodProcessor替換爲自定義實現ApiReturnValueHandler,並在ApiReturnValueHandler保存RequestResponseBodyMethodProcessor的實例。
@Component
public class ResponseBodyWrapBean implements InitializingBean {
@Autowired
private RequestMappingHandlerAdapter adapter;
@Value("${server.servlet.context-path:/}")
private String contextPath;
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers);
// 在handlers中將RequestResponseBodyMethodProcessor替換爲ApiReturnValueWrapHandler,
// 並在ApiReturnValueWrapHandler保存RequestResponseBodyMethodProcessor
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
ApiReturnValueHandler apiReturnValueHandler = new ApiReturnValueHandler(handler,contextPath);
int index = handlers.indexOf(handler);
handlers.set(index, apiReturnValueHandler);
break;
}
}
adapter.setReturnValueHandlers(handlers);
}
}
ApiReturnValueHandler實現HandlerMethodReturnValueHandler接口,並在類裏面保存RequestResponseBodyMethodProcessor的實例,使用RequestResponseBodyMethodProcessor的supportsReturnType原實現,即判斷類或方法是否有@ResponseBody註解。如果有,就會執行該handleReturnValue方法。
在handleReturnValue方法中將返回值包裝後,就繼續按RequestResponseBodyMethodProcessor的原實現繼續執行。
public class ApiReturnValueHandler implements HandlerMethodReturnValueHandler {
// 因爲RequestResponseBodyMethodProcessor被替換,所以將其保存下來,處理後在調用原邏輯
private final HandlerMethodReturnValueHandler handler;
private final String contextPath;
public ApiReturnValueHandler(HandlerMethodReturnValueHandler handler, String contextPath) {
this.handler = handler;
this.contextPath = contextPath;
}
/**
* 判斷是否支持值處理引擎
* 一定要正確,如果不正確,就永遠不會被執行
* @param returnType
* @return
*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return handler.supportsReturnType(returnType);
}
/**
* 對值進行處理,並確定是否繼續進行下一個處理引擎執行
* @param returnValue
* @param returnType
* @param mavContainer
* @param webRequest
* @throws Exception
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
String uri = ((HttpServletRequest) webRequest.getNativeRequest()).getRequestURI();
String apiUrl = contextPath + "/api";
if (uri.startsWith(apiUrl)) {
// 將api開頭的url都包裝成統一返回
Map<String, Object> result = new HashMap<>(3);
result.put("code", 0);
result.put("message", "ok");
result.put("data", returnValue);
handler.handleReturnValue(result, returnType, mavContainer, webRequest);
} else {
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
}
}
測試:
參考:
自定義統一api返回json格式(app後臺框架搭建三)
Spring MVC 使用介紹(十二)控制器返回結果統一處理
SpringMVC 流程