问题描述:
在我现在的项目中,由于人员更替,代码有些不规范。前端Web、App 对后端同一接口的请求方式可能不一样。例如,有的接口Web请求的时候,使用的是 Post 请求,数据包装为一个Json Object;但是在App端,可能使用的是Post 一个 表单数据过来。这样,后端接收数据的时候,@RequestBody 便不能满足需求。所以需要我们寻求一种解决方式同时兼容这些请求的方式。
解决方案:
自定义注解@RequestModel,重写WebMvcConfigurerAdapter并添加自定义的HandlerMethodArgumentResolver。
具体操作:
1、自定义注解
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestModel {
}
2、自定义HandlerMethodArgumentResolver:
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ArrayUtils;
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.BufferedReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class RequestHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestModel.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (StringUtils.equals(request.getMethod(), "POST")) {
// 处理Post请求
if (request.getContentType().contains("application/json")) {
// 处理 json 数据的Post请求
return this.doPostJson(parameter, request);
} else {
// 处理 form 表单类型Post请求
return this.doGetAndPostForm(parameter, webRequest);
}
} else if (StringUtils.equals(request.getMethod(), "GET")) {
// 处理Get类型的请求
return this.doGetAndPostForm(parameter, webRequest);
}
return parameter.getParameterType().newInstance();
}
/**
* 处理 Get请求、Form表单形式的Post请求
*
* @param parameter MethodParameter
* @param webRequest NativeWebRequest
* @return 解析出的Object
*/
private Object doGetAndPostForm(MethodParameter parameter, NativeWebRequest webRequest) throws IllegalAccessException, InstantiationException, IOException {
Map<String, String[]> parameterMap = webRequest.getParameterMap();
Class<?> parameterType = parameter.getParameterType();
if (parameterMap == null || parameterMap.size() <= 0) {
return parameterType.newInstance();
}
Map<String, Object> map = new HashMap<>();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String[] arr = entry.getValue();
if (ArrayUtils.isNotEmpty(arr)) {
map.put(entry.getKey(), arr[0]);
}
}
ObjectMapper mapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
String json = mapper.writeValueAsString(map);
return mapper.readValue(json, parameterType);
}
/**
* 处理 json 形式的Post请求
*
* @param parameter MethodParameter
* @param request HttpServletRequest
* @return 解析出的Object
*/
private Object doPostJson(MethodParameter parameter, HttpServletRequest request) throws IOException {
BufferedReader reader = request.getReader();
StringBuilder sb = new StringBuilder();
char[] buf = new char[1024];
int len;
while ((len = reader.read(buf)) != -1) {
sb.append(buf, 0, len);
}
Class<?> parameterType = parameter.getParameterType();
return JSON.parseObject(sb.toString(), parameterType);
}
}
3、重写WebMvcConfigurerAdapter,并添加上面自定义的HandlerMethodArgumentResolver
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
public class ApiWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new RequestHandlerMethodArgumentResolver());
super.addArgumentResolvers(argumentResolvers);
}
}
使用范例:
// 不管前端传递的是JSON还是 Form 表单,都能注入进去
@RequestMapping(value = "test")
public ResultModel test(@RequestModel TestModel testModel) {
// do something
}
最后的最后:
作者也是在不断学习之中,难免有疏漏或者错误的地方,有什么地方有问题的,欢迎留言探讨,一起进步!