SpringBoot:自定義註解實現後臺接收Json引數
阿新 • • 發佈:2021-07-18
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
我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光裡即便有過天真愚鈍,也不值得譴責。畢竟,往後的日子,還很長。不斷鼓勵自己, 天一亮,又是嶄新的起點,又是未知的征程(上校9) 逆水行舟,不進,則退!