spring controller 自定义参数解析器 HandlerMethodArgumentResolver

一、背景

在使用spring mvc 或者spring boot 框架搭建项目,和前端对接时,经常会遇到前端传给后端的参数和后端想要的参数数据格式不一致,针对post请求,大致分为两种:

  1. {}形式的json串
  2. key:value形式,多行,每行表示一个参数

注:get请求参数格式一般是:key=value&key=value的格式,使用@RequestParam的格式可以解析出对应的内容;当post请求的请求体格式为上述的2时,使用@RequestParam也可以解析,但是为格式1时,则无法解析。

二、使用HandlerMethodArgumentResolver

当格式为上述格式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; 

}
  1. supportsParameter 用于判定是否需要处理该参数分解,返回true为需要,并会去调用下面的方法resolveArgument。
  2. resolveArgument 真正用于处理参数分解的方法,返回的Object就是controller方法上的形参对象。

首先需要定义一个自定义注解,使用该注解的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;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值