SpringBoot 裡的 RequestBodyAdviceAdapter 使用問題

邢闖洋發表於2022-08-31

一、問題

之前寫過一篇《SpringBoot 介面引數解密的實現方法(使用註解)》,程式碼一直跑著都沒問題,但是最近發現入參特別長的時候,inputMessage.getBody() 這段程式碼拿不到完整的入參,會被截斷。

看了下原始碼,好像預設它轉換的時候就截斷了。

而且只有線上有問題,本地測試無法復現該問題。

二、解決辦法

看了下原始碼,RequestBodyAdviceAdapter 這個靜態類除了 beforeBodyRead 這個方法外,還有個 afterBodyRead 方法,原來我一直以為 beforeBodyReadController 層之前執行,afterBodyRead 是在 Controller 層之後執行,後來我發現我理解錯了,它們都是在 Controller 層之前執行。

並且,beforeBodyRead 方法是在 afterBodyRead 方法之前執行,走這兩個方法之前都會走一次 supports 方法來判斷是否執行。

於是我就把解密邏輯挪到了 afterBodyRead 方法裡,就不存在入參超長被截斷的問題了。

注意:剛剛另一個專案上線發現,afterBodyRead 讀取的是 Controller 中 RequestBody 的實體類,若實體類中有可為空並且設定了預設值的,比如分頁引數 pageNo 預設為1,如果客戶端沒傳,afterBodyRead 中讀取到的就是1,會解密失敗。

/**
 * 請求引數解密
 * http://www.zzvips.com/article/187109.html
 * https://www.cnblogs.com/shamo89/p/16498217.html
 */
@ControllerAdvice
@Slf4j
public class DecryptRequest extends RequestBodyAdviceAdapter {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {

        try {

            String requestBody = JSONUtils.serializeObject(body);

//            log.info("requestBody=" + requestBody);

            // 轉換為物件
            JSONObject bodyObj = JSONObject.parseObject(requestBody);

            // 定義無需解密引數
            List<String> noDecryptFiled = Arrays.asList("appClient", "channel", "version", "token", "projectId");

            // 定義解密後引數map
            HashMap<String, String> decryptParam = new HashMap<>();

            // 迴圈請求物件
            for (Map.Entry<String, Object> stringObjectEntry : bodyObj.entrySet()) {

                if (stringObjectEntry.getValue() == null) {
                    continue;
                }

                String key = stringObjectEntry.getKey();
                String value = stringObjectEntry.getValue().toString();

                // 如果是開發環境,無需解密
                if (Result.getEnv().equals("dev")) {
                    decryptParam.put(key, value);
                    continue;
                }

                // 若是無需解密引數,直接put進decryptParam
                if (noDecryptFiled.contains(stringObjectEntry.getKey())) {
                    decryptParam.put(key, value);
                    continue;
                }

                // 解密
                decryptParam.put(key, AESUtil.decrypt(value, Result.SALT));
            }

            body = JSONObject.parseObject(JSON.toJSONString(decryptParam), targetType);

        } catch (IOException e) {
            throw new BaseException(SystemErrorType.BUSINESS_ERROR, "引數解密失敗,請檢查");
        }

//        log.info("body=" + JSON.toJSONString(body));

        return super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
    }
}

參考文章

SpringMvc裡的RequestBodyAdviceAdapter使用問題

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章