SpringMVC API統一JSON格式返回

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測試
非api測試

參考:
自定義統一api返回json格式(app後臺框架搭建三)
Spring MVC 使用介紹(十二)控制器返回結果統一處理
SpringMVC 流程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章