springboot2 - HandlerMethodArgumentResolver
场景 A
@RequestParam 大家都用过,设置 required = true,告诉前端这个参数是必传的,
但是用过的也基本了解,前端传个空字符串,校验就跳过了,实用性不佳。
class Controller{
@ResponseBody
@RequestMapping("/data")
public Map<String, Object> data(@RequestParam(required = true) String b) {
return new HashMap<>();
}
}
场景 B
能不能通过一些注解,把一些缓存中的数据,优雅地注入进来,比如:当前登录的用户信息。
class Controller{
@ResponseBody
@RequestMapping("/data")
public Map<String, Object> data(@Authority User user) {
return new HashMap<>();
}
}
基本功能介绍
此时你需要写一个 AOP 切面,HandlerMethodArgumentResolver,直译是:处理函数参数的分解器。
import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
/**
* @since 3.1
*/
public interface HandlerMethodArgumentResolver {
/**
* 设置一些规则,返回 true 的时候,才执行 resolveArgument() 函数
*/
boolean supportsParameter(MethodParameter parameter);
/**
* 从给定的 request 中获取参数,返回值最终会通过参数,传递给 controller 的 method。
*/
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
spring 配置
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="xxxxx全类名"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
springboot 配置
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;
/**
* Created by 12614 on 2018/5/11.
*/
@Configuration
public class ApplicationConfigurer extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(new TestArgumentResolver());
}
}
代码样例
针对上面的场景 A,设计一个注解,用于非空校验。
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
/**
* 客户端参数校验注解
* <p>
* 同类注解:{@link javax.validation.constraints.NotEmpty}
*
* @author Created by Mr.css on 2018/5/11.
*/
@Documented
@Target(value = ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface Params {
/**
* 参数名
*/
@AliasFor("name")
String value() default "";
/**
* 参数名
*/
@AliasFor("value")
String name() default "";
/**
* 提示用参数名(中文名称 Label)
*/
String label() default "";
/**
* 不为空
*/
boolean notEmpty() default true;
/**
* 最大长度
*/
int length() default Integer.MAX_VALUE;
/**
* 默认值
*/
String defaultValue() default "";
}
参数处理器
import cn.seaboot.commons.core.CommonUtils;
import cn.seaboot.commons.exception.BizException;
import cn.seaboot.commons.lang.Null;
import org.jetbrains.annotations.NotNull;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
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;
/**
* 参数校验
* <p>
* 请求参数校验工具,主要用于补充 @RequestParam 的功能,增加 NotEmpty 校验
* <p>
* 同类注解:{@link javax.validation.constraints.NotEmpty}
*
* @author Mr.css on 2018-05-11.
*/
public class ArgumentResolver implements HandlerMethodArgumentResolver {
/**
* {@link org.springframework.boot.autoconfigure.web.format.WebConversionService}
*/
private final ConversionService conversionService;
/**
* ConversionService is necessary, you can get ConversionService instance from spring-context
*
* @param conversionService form spring-context
*/
public ArgumentResolver(@NotNull ConversionService conversionService) {
this.conversionService = conversionService;
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
Params requestParam = parameter.getParameterAnnotation(Params.class);
return requestParam != null;
}
@Override
@SuppressWarnings("all")
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer modelAndViewContainer,
NativeWebRequest nativeWebRequest,
WebDataBinderFactory webDataBinderFactory) {
HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
Params params = methodParameter.getParameterAnnotation(Params.class);
// get parameter name
String paramName = params.value();
if ("".equals(paramName)) {
paramName = methodParameter.getParameterName();
}
// get value by parameter name
String clientParam = request.getParameter(paramName);
if (clientParam == null) {
clientParam = params.defaultValue();
}
if (params.notEmpty()) {
if (CommonUtils.isEmpty(clientParam)) {
// if value is empty, throw a exception
throw new BizException(CommonUtils.nvl(params.label(), paramName) + " 不能为空!");
} else if (clientParam.length() > params.length()) {
throw new BizException(CommonUtils.nvl(params.label(), paramName) + " 超出最大长度限制:" + params.length() + "!");
}
}
try {
// 对象类型转换
return conversionService.convert(clientParam, TypeDescriptor.forObject(clientParam), new TypeDescriptor(methodParameter));
} catch (Exception e) {
throw new BizException("客户端参数格式转换失败:" + paramName, e);
}
}
}
使用方式
class Controller{
@ResponseBody
@RequestMapping("/data")
public Map<String, Object> data(@Params String b) {
return new HashMap<>();
}
}
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!