在使用spring mvc 或者spring boot 框架搭建项目,和前端对接时,经常会遇到前端传给后端的参数和后端想要的参数数据格式不一致,针对post请求,大致分为两种:
注:get请求参数格式一般是:key=value&key=value的格式,使用@RequestParam的格式可以解析出对应的内容;当post请求的请求体格式为上述的2时,使用@RequestParam也可以解析,但是为格式1时,则无法解析。
当格式为上述格式1时,可以使用@RequestBody 获取整个字符串,然后解析成jsonObject,但是这样写和代码中使用@RequestParam没有保持一致,整体看着很变扭。是否可以向@RequestParam一样自定义一个解析器解析请求体呢?答案是可以的,在spring 中主要是需要实现接口HandlerMethodArgumentResolver
下面是该接口的定义:
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
首先需要定义一个自定义注解,使用该注解的controller参数将会用自定义的方法解析;该注解类比@RequestParam
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface JSONParam {
String value();
boolean required() default true;
String defaultValue() default "";
}
自定义解析类
public class JSONParamResolver implements HandlerMethodArgumentResolver {
private static final String JSON_REQUEST_BODY = "JSON_REQUEST_BODY";
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
return methodParameter.hasParameterAnnotation(JSONParam.class);
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
String body = getRequestBody(nativeWebRequest);
JSONObject jsonObject;
try {
jsonObject = JSON.parseObject(body);
} catch (Exception e) {
return new Exception("请求体格式解析异常");
}
//获取注解配置
JSONParam jsonParam = methodParameter.getParameterAnnotation(JSONParam.class);
String paramName = jsonParam.value();
if (!jsonObject.containsKey(paramName) && jsonParam.required()) {
// 如果json没有包含该数据,并且是必填,则直接操作
throw new MissingServletRequestParameterException(paramName,
methodParameter.getNestedParameterType().getSimpleName());
}
//以下代码的作用是当请求参数中没有方法对应的参数,则将默认参数转换为需求的类型,然后返回
Object arg = jsonParam.defaultValue();
if (!jsonObject.containsKey(paramName) && !jsonParam.required()) {
if (webDataBinderFactory != null) {
WebDataBinder binder = webDataBinderFactory.createBinder(nativeWebRequest, null, jsonParam.value());
try {
arg = binder.convertIfNecessary(arg, methodParameter.getParameterType(), methodParameter);
}
catch (ConversionNotSupportedException ex) {
throw new MethodArgumentConversionNotSupportedException(arg, ex.getRequiredType(),
jsonParam.value(), methodParameter, ex.getCause());
}
catch (TypeMismatchException ex) {
throw new MethodArgumentTypeMismatchException(arg, ex.getRequiredType(),
jsonParam.value(), methodParameter, ex.getCause());
}
}
return arg;
}
try {
return jsonObject.getObject(paramName, methodParameter.getNestedParameterType());
} catch (Exception e) {
throw new MissingServletRequestParameterException(paramName,
methodParameter.getNestedParameterType().getSimpleName());
}
}
private String getRequestBody(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
String jsonBody = (String) servletRequest.getAttribute(JSON_REQUEST_BODY);
if (jsonBody == null) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(servletRequest.getInputStream()));
jsonBody = IOUtils.toString(reader);
servletRequest.setAttribute(JSON_REQUEST_BODY, jsonBody);
} catch (IOException e) {
LOGGER.error("解析请求体出错", e);
}
}
return jsonBody;
}
}