SpringBoot:自定義注解實現后台接收Json參數


0.需求

在實際的開發過程中,服務間調用一般使用Json傳參的模式,SpringBoot項目無法使用@RequestParam接收Json傳參

只有@RequestBody支持Json,但是每次為了一個接口就封裝一次實體類比較麻煩

如果使用Map來進行參數接收,則會導致參數不可控,會在接口中新增較多判斷進行入參控制

 

其次,在實際的開發過程中,我們偶爾會傳入兩個實體類,如果使用@RequestBody也會出錯

因為傳入的參數只能夠讀取一次,一般這里也會封裝一次實體類,不夠方便

也有重寫HttpServletRequestWrapper的處理辦法,但不能解決上一個問題

1.思路

因為一個注解只能讀取一次,按照重寫HttpServletRequestWrapper的思路,將請求中的Json參數進行緩存

另外自定義一個注解,來把參數進行注入。

1.1.自定義@JsonFmt注解

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface JsonFmt {
    /**
     * 值
     */
    String value() default "";

    /**
     * 是否必須
     */
    boolean require() default true;
}

這里的值,不是給參數的默認值(defaultValue),而是類似於@RequestParam注解中的value、name,是用來指定入參的key

1.2.自定義注解的實現類

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class JsonFmtHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {

    //自定義key
    private static final String KEY = "TEST_JSON_BODY_KEY";
    private static ObjectMapper objectMapper = new ObjectMapper();


    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(JsonFmt.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        JsonFmt jsonFmt = parameter.getParameterAnnotation(JsonFmt.class);
        JSONObject jsonObject = getJsonObject(webRequest);

        String value = getParamName(parameter,jsonFmt);
        boolean require = jsonFmt.require();
        Object paramValue = getParamValue(jsonObject,value);

        if (paramValue == null && require) {
            throw new Exception("parameter[" + value + "]不能為空。");
        }
        if (paramValue == null) {
            return null;
        }

        Class<?> classType = parameter.getParameterType();

        if (paramValue.getClass().equals(JSONObject.class)){
            paramValue = objectMapper.readValue(paramValue.toString(),classType);
        }

        return paramValue;
    }

    private String getParamName(MethodParameter parameter, JsonFmt jsonFmt) {
        String value = jsonFmt.value();
        if (StringUtils.isEmpty(value)) {
            value = parameter.getParameterName();
        }
        return value;
    }

    private Object getParamValue(JSONObject jsonObject,String value) {
        for (String key: jsonObject.keySet()) {
            if(key.equalsIgnoreCase(value)){
                return jsonObject.get(key);
            }
        }
        return null;
    }

    private JSONObject getJsonObject(NativeWebRequest webRequest) throws Exception {
        String jsonBody = (String) webRequest.getAttribute(KEY, NativeWebRequest.SCOPE_REQUEST);
        if(StringUtils.isEmpty(jsonBody)){
            HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
            BufferedReader reader = request.getReader();
            StringBuilder sb = new StringBuilder();
            char[] buf = new char[1024];
            int rd;
            while ((rd = reader.read(buf)) != -1) {
                sb.append(buf, 0, rd);
            }

            jsonBody = sb.toString();

            if(StringUtils.isEmpty(jsonBody)){
                Map<String,String[]> params = request.getParameterMap();

                Map tmp = new HashMap();
                for (Map.Entry<String,String[]> param:params.entrySet()) {
                    if(param.getValue().length == 1){
                        tmp.put(param.getKey(),param.getValue()[0]);
                    }else{
                        tmp.put(param.getKey(),param.getValue());
                    }

                }
                jsonBody = JSON.toJSONString(tmp);
            }

            webRequest.setAttribute(KEY, jsonBody, NativeWebRequest.SCOPE_REQUEST);
        }

        return JSONObject.parseObject(jsonBody);
    }
}

方法說明:

supportsParameter:說明支持的注解,只要方法參數有@JsonFmt就啟用該實現類

resolveArgument:解決方法,注解的具體實現

getJsonObject:獲取請求體,這里的實現邏輯就是從請求中獲取Json體,如果沒有獲取到,則從請求參數中獲取(兼容From模式),將請求體封裝為JsonObject

getParamName:獲取注解參數的key,先獲取注解的value,如果為空,則使用方法參數的名稱

getParamValue:這個可以不加,我這里是為了讓key不區分大小寫,如果需要區分,直接使用jsonObject.get(key)即可

1.3.加入自定義注解

import com.example.demo.jsonfmt.JsonFmtHandlerMethodArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new JsonFmtHandlerMethodArgumentResolver());
    }

}

2.使用

到這里我們就能愉快的使用我們的自定義注解@JsonFmt來進行參數接收了

目前在Json傳參中,能完美的接收實體類、List、Map以及其他基礎類型

在Form傳參中,能夠支持List、Map以及其他基礎類型,對於實體類暫時還不能兼容

因為后台接收到的是Map,不容易區分哪些是實體類的字段,無法進行填充,這種建議使用@RequestBody

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM