基於@RequestBody註解只能注入物件和map的解決
目錄
- @RequestBody註解只能注入物件和map的問題
- 1、自定義一個適應於這種情況的註解@Requeston
- 2、自定義RequestJsonHandlerMethodArgumentResolver
- 3、將上述配置應用到spring專案中
- 4、配置完成了,簡單使用
- @RequestBody註解的使用問題
- 先看一下@RequestBody的作用
- 個人總結:
@RequestBody註解只能注入物件和map的問題
前後端分離開發模式下,前後端資料互動全部採用json,所以在後端在採用spring框架的時候都會使用@ResponseBody(後端返回引數封裝為json格式)和@RequestBody(前端請求攜帶json引數解析)註解。
但是在實際開發中,往往@RequestBody的使用會比較令人難受(超級難受),因為它spring官方只支援到將json解析為一個定義好的物件或者是一個通用性的map,而我們實際專案中經常傳遞的引數僅僅是一個或者是兩個引數,這樣的引數封裝程物件總是有點大材小用的感覺,並且還消耗效能,而使用map又感覺操作比較繁瑣,那這樣的話可不可以使用簡單的類似於@PathVariable(value="")這樣的模式取請求資料呢????當然可以!!
1、自定義一個適應於這種情況的註解@RequestJson
package cn.annotation; import .lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Title: RequestJson * @date 2018年9月10日 * @version V1.0 * Description: 自定義請求json資料解析註解(主要解決單引數傳遞) */ @Target(ElementType.PARAMETER)//使用在引數上 @Retention(RetentionPolicy.Rhttp://www.cppcns.comUNTIME)//執行時註解 public @interface RequestJson { /** * 是否必須出現的引數 */ boolean required() default true; /** * 當value的值或者引數名不匹配時,是否允許解析最外層屬性到該物件 */ boolean parseAllFields() default true; /** * 解析時用到的JSON的key */ String value() default ""; }
2、自定義RequestJsonHandlerMethodArgumentResolver
實現HandlerMethodArgumentResolver(spring解析資料的介面)
package cn.config;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import cn.annotation.RequestJson;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.MethodParameter;
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.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
/**
* Title: RequestJsonHandlerMethodArgumentResolver
* @date 2018年9月10日
* @version V1.0
* Description: 自定義解析json資料
*/
public class RequestJsonHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private static final String JSONBODY_ATTRIBUTE = "JSON_REQUEST_BODY";
/**
* 設定支援的方法引數型別
* @param parameter 方法引數
* @return 支援的型別
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 支援帶@RequestJson註解的引數
return parameter.hasParameterAnnotation(RequestJson.class);
}
/**
* 引數解析,利用fastjson
* 注意:非基本型別返回null會報空指標異常,要通過反射或者JSON工具類建立一個空物件
*/
@Override
public Object resolveArgument(MethodParameter parameter,ModelAndViewContainer mavContainer,NativeWebRequest webRequest,WebDataBinderFactory binderFactory) throws Exception {
String jsonBody = getRequestBody(webRequest);
JSONObject jsonObject = JSON.parseObject(jsonBody);
// 根據@RequestJson註解value作為json解析的key
RequestJson parameterAnnotation = parameter.getParameterAnnotation(RequestJson.class);
//註解的value是JSON的key
String key = parameterAnnotation.value();
Object value = null;
// 如果@RequestJson註解沒有設定value,則取引數名FrameworkServlet作為json解析的key
if (StringUtils.isNotEmpty(key)) {
value = jsonObject.get(key);
// 如果設定了value但是解析不到,報錯
if (value == null && parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present",key));
}
} else {
// 註解為設定value則用引數名當做json的key
key = parameter.getParameterName();
value = jsonObject.get(key);
}
Class<?> parameterType = parameter.getParameterType();
// 通過註解的value或者引數名解析,能拿到value進行解析
if (value != null) {
if (isBasicDataTypes(parameterType)) {
return value;
}
return JSON.parseObject(value.toString(),parameterType);
}
// 解析不到則將整個json串解析為當前引數型別
if (isBasicDataTypes(parameterType)) {
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present",key));
} else {
return null;
}
}
Object result = parameterType.newInstance();
// 非基本型別,不允許解析所有欄位,返回null
if (!parameterAnnotation.parseAllFields()) {
// 如果是必傳引數拋異常
if (parameterAnnotation.required()) {
throw new IllegalArgumentException(String.format("required param %s is not present",key));
}
// 否則返回空物件
return result;
}
// 非基本型別,允許解析,將外層屬性解析
result = JSON.parsewww.cppcns.comObject(jsonObject.toString(),parameterType);
// 如果非必要引數直接返回,否則如果沒有一個屬性有值則報錯
if (!parameterAnnotation.required()) {
return result;
}else{
boolean haveValue = false;
Field[] declaredFields = parameterType.getDeclaredFields();
for(Field field : declaredFields){
field.setAccessible(true);
if(field.get(result) != null){
haveValue = true;
break;
}
}
if(!haveValue){
客棧 throw new IllegalArgumentException(String.format("required param %s is not present",key));
}
return result;
}
}
/**
* 基本資料型別直接返回
*/
@SuppressWarnings("rawtypes")
private boolean isBasicDataTypes(Class clazz) {
Set<Class> classSet = new HashSet<>();
classSet.add(String.class);
classSet.add(Integer.class);
classSet.add(Long.class);
classSet.add(Short.class);
classSet.add(Float.class);
classSet.add(Double.class);
classSet.add(Boolean.class);
classSet.add(Character.class);
return classSet.contains(clazz);
}
/**
* 獲取請求體JSON字串
*/
private String getRequestBody(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
// 有就直接獲取
String jsonBody = (String) webRequest.getAttribute(JSONBODY_ATTRIBUTE,NativeWebRequest.SCOPE_REQUEST);
// 沒有就從請求中讀取
if (jsonBody == null) {
try {
jsonBody = IOUtils.toString(servletRequest.getReader());
webRequest.setAttribute(JSONBODY_ATTRIBUTE,jsonBody,NativeWebRequest.SCOPE_REQUEST);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return jsonBody;
}
}
3、將上述配置應用到spring專案中
重寫addArgumentResolvers方法
package cn.config; import java.nio.charset.Charset; import java.util.List; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; /** * Title: WebConfig * @date 2018年9月10日 * @version V1.0 * Description: 將自定義註解配置到spring */ @Configuration @SuppressWarnings("deprecation") public class WebConfig extends WebMvcConfigurerAdapter{ @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new RequestJsonHandlerMethodArgumentResolver()); } @Bean public HttpMessageConverter<String> responseBodyConverter() { return new StringHttpMessageConverter(Charset.forName("UTF-8")); } @Override public void configureMessageConverters(List<HttpMessageConverter<?www.cppcns.com;>> converters) { super.configureMessageConverters(converters); converters.add(responseBodyConverter()); } }
4、配置完成了,簡單使用
單引數:(不加value預設為引數名)
或者:
多引數:
@RequestBody註解的使用問題
今天遇到的問題:@RequestBody的使用問題
先看一下@RequestBody的作用
我想獲取json字串某個欄位值,看截圖:
看一下控制檯的輸出資訊:
what ?這什麼情況,為什麼拿到的是整個json字串,然後我繼續測試
給了一個400
what ?這又是什麼情況 (好像只能有一個@RequestBody)我想引數如果是整形的話能不能獲取,我繼續進行測試程式碼:
傳參:
又給了一個400 (好像只能是String型別) 測試引用型別物件
程式碼:
傳參:
控制檯列印:
測試成功。
個人總結:
1) 一個方法只能有一個@RequestBody
2) 如果接收引數是字串型別的,獲取的是整個json字串
3) 如果接受的引數是引用物件,@requestBody User user 會將json字串中的值賦予user中對應的屬性上
需要注意的是,json字串中key必須和User物件的屬性名對應
以上為個人經驗,希望能給大家一個參考,也希望大家多多支援我們。