ResponseBodyAdvice踩坑及解決

場景

通過ResponseBodyAdvice實現Rest接口的日志統一管理

正文

ResponseBodyAdvice原理自己百度,代碼比較少但是我實踐的時候發現有幾個坑需要註意一下

@RestControllerAdvice(basePackages = "com.alan.api.controller")
public class ApiResponseBodyAdvice implements ResponseBodyAdvice {
    static org.slf4j.Logger logger = LoggerFactory.getLogger("logback_api");
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
                returnType.hasMethodAnnotation(ResponseBody.class));
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                  Class selectedConverterType, ServerHttpRequest serverHttpRequest, ServerHttpResponse response) {
        HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        if(request != null){
            Object obj = request.getSession().getAttribute(BaseController.session_user);
            String path = request.getServletPath();
            if (StringUtils.isBlank(path)) {
                path = request.getPathInfo();
            }
            if (obj != null) {
                path = request.getPathInfo();
                logger.info("userId:"+ ((DataUser) obj).getUserId());
            }
            logger.info("url:"+ path);
            logger.info("request:"+ JSON.toJSONString(request.getParameterMap()));
            logger.info("response:"+body);
        }
        return body;
    }
}

沒瞭就這麼簡單

生效可能情況

1.ApiResponseBodyAdvice bean沒有scan,沒有什麼配置

2.如果Controller的註解為@Controller,生效的方法為@ResponseBody

3.supports()支持類型返回false,beforeBodyWrite()不調用

spring切面接口ResponseBodyAdvice的分析及使用

ResponseBodyAdvice接口屬於springMVC 和springBoot框架基礎的底層切面接口;實現這個接口的類,可以修改直接作為 ResponseBody類型處理器的返回值,即進行功能增強。

1、有兩種類型的處理器會將返回值作為ResponseBody:

返回值為HpptEntity

加瞭@ResponseBody或@RestController註解,

實現瞭這個接口的類,處理返回的json值在傳遞給 HttpMessageConverter之前;應用場景在spring項目開發過程中,對controller層返回值進行修改增強處理。比如返回值5,需要封裝成

{“code”:”0″,”data”:5,,”msg”:”success”}格式返回前端

接口源碼如下:

public interface ResponseBodyAdvice<T> { 
    / * *
    *該組件是否支持給定的控制器方法返回類型
    *和選擇的{@code HttpMessageConverter}類型。
    返回類型
    * @param converterType選擇的轉換器類型
    * @return {@code true}如果{@link #beforeBodyWrite}應該被調用;
    * {@code false}否則
    * /
	boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
    / * *
    *在{@code HttpMessageConverter}被選中之後和之前調用
    *它的write方法被調用。
    * @param body要寫入的主體
    控制器方法的返回類型:
    * @param selectedContentType通過內容協商選擇的內容類型
    * @param selectedConverterType選擇寫入響應的轉換器類型
    * @param request當前請求
    * @param response當前響應
    * @return傳入的主體或修改過的(可能是新的)實例
   * /
	@Nullable
	T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType,
			ServerHttpRequest request, ServerHttpResponse response);
}

2、應用場景在spring項目開發過程中

對controller層返回值進行修改增強處理。比如返回值5,需要封裝成

{“code”:”0″,”data”:5,,”msg”:”success”} 格式返回前端

controller層業務代碼:

@RestController //此註解包含@ResponseBody註解
@RequestMapping("/nandao")
public class ResponseBodyAdviceController {
 
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public int hello() {
         //業務代碼省略
        return 5;
    } 
}

實現ResponseBodyAdvice接口的切面類:

/**
*此註解針對controller層的類做增強功能,即對加瞭@RestController註解的類進行處理
*/
@ControllerAdvice(annotations = RestController.class)
public class RestResultWrapper implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
 
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
 
            //定義一個統一的返回類
            RestResult responseResult = new RestResult( "0", body, "success");
             //如果handler處理類的返回類型是String(即控制層的返回值類型),為瞭保證一致性,這裡需要將ResponseResult轉回去
            if(body instanceof String) {
                return JSON.toJSONString(responseResult);
            }
           //封裝後的數據返回到前端頁面
            return JSONObject.toJSON(responseResult);       
    } 
}

返回公共類的創建:

/**
 * @author nandao
 * Created on 2021/1/12-21:47.
 * 統一返回Rest風格的數據結構
 */
public class RestResult<T>  implements Serializable {
 
    /**
     * 成功的code碼
     */
    private String code = "2000";
    /**
     * 成功時返回的數據,失敗時返回具體的異常信息
     */
    private T data;
    /**
     * 請求失敗返回的提示信息,給前端進行頁面展示的信息
     */
    private String message ; 
    public RestResult() {
    }
 
    @Override
    public String toString() {
 
        return "RestResult{" +
                "code='" + code + '\'' +
                ", data=" + data +
                ", message=" + message +
                '}';
    }
 
    public RestResult(String code, T data, String message) {
        this.code = code;
        this.data = data;
        this.message = message;
    }
 
    public String getCode() {
        return code;
    }
 
    public void setCode(String code) {
        this.code = code;
    }
 
    public T getData() {
        return data;
    }
 
    public void setData(T data) {
        this.data = data;
    }
 
    public String getMessage() {
        return message;
    }
 
    public void setMessage(String message) {
        this.message = message;
    }
}

到此切面增強功能就實現瞭,可以直接在實戰項目中使用。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: