源码剖析@ApiImplicitParam对@RequestParam的required属性的侵入性 (3)

聚集重点在请求参数的读取校验方面,首先看org.springframework.web.method.annotation.AbstractNamedValueMethodArgumentResolver类的resolveArgument方法:

@Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 留意此方法调用 NamedValueInfo namedValueInfo = getNamedValueInfo(parameter); MethodParameter nestedParameter = parameter.nestedIfOptional(); Object resolvedName = resolveStringValue(namedValueInfo.name); if (resolvedName == null) { throw new IllegalArgumentException( "Specified name must not resolve to null: [" + namedValueInfo.name + "]"); } // 后面暂时省略 }

getNamedValueInfo方法的实现如下:

/** * Obtain the named value for the given method parameter. */ private NamedValueInfo getNamedValueInfo(MethodParameter parameter) { NamedValueInfo namedValueInfo = this.namedValueInfoCache.get(parameter); if (namedValueInfo == null) { namedValueInfo = createNamedValueInfo(parameter); namedValueInfo = updateNamedValueInfo(parameter, namedValueInfo); this.namedValueInfoCache.put(parameter, namedValueInfo); } return namedValueInfo; }

进入createNamedValueInfo(parameter)方法时,这部分代码如下:

@Override protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { RequestParam ann = parameter.getParameterAnnotation(RequestParam.class); return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo()); } /** * NamedValueInfo的定义 * Represents the information about a named value, including name, whether it's required and a default value. */ protected static class NamedValueInfo { private final String name; private final boolean required; @Nullable private final String defaultValue; public NamedValueInfo(String name, boolean required, @Nullable String defaultValue) { this.name = name; this.required = required; this.defaultValue = defaultValue; } }

这段代码很关键,这里只读取@RequestParam注解,不会读@ApiImplicitParam注解,所以@ApiImplicitParam注解不会影响@RequestParam的属性,并且无论是从swagger doc过来的请求,还是postman过来的请求,都执行这一段代码,最终读取注解的结果用CurrenctHashMap存储,key的格式是method 'xxx' parameter y,xxx为方法名,y为参数的顺序号,如method 'auth' parameter 0,基本上可以保证唯一性。

阶段性总结

源码阅读到这里,基本上可以验证前面提及的小结论的前2条,引用一下:

@ApiImplicitParam的required参数不会对@RequestParam的required值造成侵入,它们俩不相关。

@ApiImplicitParam的required参数会影响swagger doc的js逻辑判断,为空校验是在js层面上完成的。

@RequestParam的required参数默认情况下只会校验是否有该参数名,不校验它是否有值。

前面2个问题已经从源码中找到解释,来看第3个问题:如果参数设置required=true,但只是要求参数名存在,如果此字段是Long类型或Integer类型,写成uid=或'uid',也能通过校验,最终进入方法后,还是得手动写代码进行为空校验,这显然不是我们想要的结果?该如何解决呢?

请求参数data bind的问题

接上一节,如果这样通用的参数,得挨个判断是否为空,这样的做法就有点难受了,有没有更好的解决办法呢?预期的实现效果是字段加上require=true后,Long类型或其他数值类型可以把"",null过滤掉,要不然require还有什么意义呢?

解决方法有两个思路:

POST请求方法中将多个参数封装到一个POJO类里,用@RequestBody声明,POJO类中可以使用@Validator框架的@NotNull等注解,并在参数前声明@Valid。

自定义参数绑定规则扩展。

方案2更通用一些,适用GET、POST请求,并且原有的单个参数声明无需封装到POJO类里。

官网本身提供自定义参数绑定的扩展,见https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#mvc-ann-initbinder

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpfyzp.html