引入
在springboot中,我们可以用@RequestBody获取请求中的json数据。但是,我们平常会客制化请求的JSON数据。通常,数据可能是这样:
{
"header: {
"deviceId": "deviceId",
"sessionId": "sessionId"
}
"body": {
"userId": "mycano",
"password": "mycano"
}
}
这里,header中会存放用户的相关信息,比如sessionId等内容,而body中则会存放前后端交互的具体数据。 若在此情况下,如果我们使用上述的客制化json数据,那么存在@RequestBody无法获取数据的情况。
比如,当我的请求数据为:
{
"userId": "123112",
"password": "odadasfgwf"
}
可以看到,在图片中我们获取到post请求传送给后端的数据。 而当我们的请求数据为:
{
"body":{
"userId": "123112",
"password": "odadasfgwf"
}
}
此时,我们通过@RequestBody是无法获取到数据的,具体如下图所示。
为了解决这个问题,我们可以通过定制化基本的接收数据的DTO,在DTO中通过反射赋值,便能获取到前端的数据。
解决过程
通过反射获取参数的值,我们需要完成以下几步:
- 提前获取Request中的数据流,并保存数据流。这个过程可以在Filter中完成,也可以在interceptor中完成,具体看项目的需要。其中filter内完成可以参考https://blog.nowcoder.net/n/8dc8fb4c78994d30a6439125d1c78001。
- 客制化基本的RequestDTO类,该类需要所有的入参类继承。并在RequestDTO中完成请求参数的赋值。
在这里,我们重点讲如何客制化基本的RequestDTO。首先,我们在前面已经获取了数据流,并将数据流的结果保存在request的BODY属性中。因此,在这里我们会获取这个数据,并通过反射获取。
@Getter
@Setter
public class RequestDTO implements Serializable {
protected HttpServletRequest request;
public RequestDTO(){
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(null != requestAttributes){
request = requestAttributes.getRequest();
}else {
request = null;
return;
}
Object body = request.getAttribute(RequestConstant.BODY);
if(null != body){
RequestUtils.invoke(this, body.toString());
}
}
}
其中,我们的RequestUtils.invoke(this, body.toString());是在RequestUtils类中,具体为:
public class RequestUtils{
/**
* 通过反射对DTO中的变量赋值,如果变量为类下的类,则该类需要用public static修饰。
* @param object
* @param body
* @throws AppTransException
*/
public static void invoke(Object object, String body) throws AppTransException {
Map<String, Object> paramsMap = JSONObject.parseObject(body, Map.class);
Class<?> clz = object.getClass();
for (Map.Entry<String, Object> entry: paramsMap.entrySet()){
String key = entry.getKey();
Object value = entry.getValue();
Field field;
try {
field = clz.getDeclaredField(key);
}catch (NoSuchFieldException e){
log.info("no field: {}", key);
continue;
}
Method method;
String methodName = null;
try {
methodName = "set" + key.substring(0, 1).toUpperCase() + key.substring(1);
method = clz.getDeclaredMethod(methodName, field.getType());
}catch (NoSuchMethodException e){
log.info("no method: {}", methodName);
continue;
}
try {
if(value instanceof Map){
Class aClass = Class.forName(field.getType().getName());
Constructor constructor = aClass.getConstructor();
Object o = constructor.newInstance();
invoke(o, value.toString());
method.invoke(object, o);
}else {
method.invoke(object, value);
}
}catch (InvocationTargetException | IllegalAccessException e){
log.error("method {} can not invoke.", methodName);
} catch (Exception e) {
String errorMsg = "类" + object.getClass().getName() + "." + key + "需要由public static修饰,否则无法反射";
log.error(errorMsg);
throw new AppTransException(ErrorEnum.CODING_ERROR, errorMsg);
}
}
}
}
在修改完项目后,我们可以测试下。其中请求的数据如下:
{
"body":{
"userId": "123112",
"password": "odadasfgwf"
}
}
在controller中,我们也获取到了请求的数据: