• springmvc 参数校验/aop失效/@PathVariable 参数为空


    添加依赖

    1 <!-- 参数校验 -->
    2 <dependency>
    3     <groupId>org.hibernate.validator</groupId>
    4     <artifactId>hibernate-validator</artifactId>
    5     <version>6.0.17.Final</version>
    6 </dependency>

    一.基本类型参数(String)校验

    1.注解要写在接口中,实现类会自动继承,如果实现类的某个重写方法没有加上了注解,接口中却没有定义,运行时会产生redefine异常

    接口:

    User getUserById(@NotNull(message = "uid不能为null") @Min(value = 1,message = "uid不合法") Integer id);
    实现类:

    @Override
    public User getUserById(@NotNull(message = "uid不能为null") @Min(value = 1,message = "uid不合法") Integer id) {
    return userMapper.getUserById(id);
    }

    Controller(restful风格最容易出的问题就是参数为空,只要不传就是404,不要尝试:xxx/getUserById/null,这种写法是400,给url设置null没有意义,解决方式很简单,给Controller增加一个映射路径即可,空参数导致的bind异常可以用在全局异常处理器捕获即可,当然你在web.xml中统一处理404也可以):

    1 @RequestMapping(value = {"/getUserById/{uid}","/getUserById"})
    2 public @ResponseBody Object getUserById(@PathVariable Integer uid) {
    3     return userService.getUserById(uid);
    4 }

    2.提供校验器,自定义异常(可选),全局异常处理器

    校验器:

    1 public class ParamsValidator {
    2 
    3     public static ExecutableValidator getValidator() {
    4         ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    5         return validatorFactory.getValidator().forExecutables();
    6     }
    7 }
    View Code

    自定义异常(可选):

     1 public class ValidParamException extends RuntimeException {
     2     private static final long serialVersionUID = 1L;
     3 
     4     private Set<ConstraintViolation<Object>> validateResult;
     5 
     6     public ValidParamException() {
     7         super();
     8     }
     9 
    10     public ValidParamException(String message) {
    11         super(message);
    12     }
    13 
    14     public ValidParamException(Set<ConstraintViolation<Object>> validateResult) {
    15         this.validateResult = validateResult;
    16     }
    17 
    18     public Set<ConstraintViolation<Object>> getValidateResult() {
    19         return validateResult;
    20     }
    21 
    22     public void setValidateResult(Set<ConstraintViolation<Object>> validateResult) {
    23         this.validateResult = validateResult;
    24     }
    25 
    26     @Override
    27     public String getMessage() {
    28         return validateResult.iterator().next().getMessage();
    29     }
    30 
    31     public Map<Object, String> getErrorMap() {
    32         Map<Object, String> map = new HashMap<Object, String>(6);
    33         Iterator<ConstraintViolation<Object>> iterator = validateResult.iterator();
    34         while (iterator.hasNext()) {
    35             ConstraintViolation<Object> cons = iterator.next();
    36             Path propertyPath = cons.getPropertyPath();
    37             String message = cons.getMessage();
    38             map.put(propertyPath, message);
    39         }
    40         return map;
    41     }
    42 
    43 }
    View Code

    全局异常处理器:

     1 @ControllerAdvice
     2 @EnableWebMvc
     3 public class GlobalExceptionHandler {
     4     // 单参数校验
     5     @ExceptionHandler(ValidParamException.class)
     6     public @ResponseBody Map<Object, String> validParamException(HttpServletRequest req, ValidParamException vpe) {
     7         return vpe.getErrorMap();
     8     }
     9 
    10     // 对象类型参数校验
    11     @ExceptionHandler(MethodArgumentNotValidException.class)
    12     public @ResponseBody Map<Object, String> methodArgumentNotValidException(MethodArgumentNotValidException ex) {
    13         String parameterName = ex.getParameter().getParameterName();
    14         Map<Object, String> map = new HashMap<Object, String>(6);
    15         map.put(parameterName, ex.getLocalizedMessage());
    16         return map;
    17     }
    18 
    19     // pathvariable不传递参数时抛出的异常
    20     @ExceptionHandler(ServletRequestBindingException.class)
    21     public @ResponseBody Map<Object, String> servletRequestBindingException(ServletRequestBindingException ex) {
    22         Map<Object, String> map = new HashMap<Object, String>(6);
    23         map.put("error", ex.getLocalizedMessage());
    24         return map;
    25     }
    26 }

    3.使用aop进行拦截

    关于aop"失效"的问题有几点说明:

    1)如果拦截controller,那么aop的配置要写在springmvc的配置文件中,拦截其他层(如service)写在spring的配置文件中

    2)被拦截的类必须也被spring管理否则无法拦截成功

    3)开启注解扫描时,springmvc只扫描@Controller类型的注解,其他的如@Service,@Repository注解由spring进行扫描

    aop:

     1 @Component
     2 @Aspect
     3 public class UserAspect {
     4 
     5     private ExecutableValidator validator = ParamsValidator.getValidator();
     6 
     7     @Pointcut("execution (* cn.tele.service.*.*(..))")
     8     private void pt() {
     9     }
    10 
    11     @Before("pt()")
    12     public void checkParams(JoinPoint jp) {
    13 
    14         Object target = jp.getTarget();
    15         Object[] params = jp.getArgs();
    16         MethodSignature methodSignature = (MethodSignature) jp.getSignature();
    17 
    18         String[] paramNames = methodSignature.getParameterNames();
    19         Method method = methodSignature.getMethod();
    20 
    21         Set<ConstraintViolation<Object>> validateResult = validator.validateParameters(target, method, params);
    22         if (!validateResult.isEmpty()) {
    23             throw new ValidParamException(validateResult);
    24         }
    25     }
    26 
    27 }
    View Code

    4.测试结果:

    1)传入-1

     2)不传

     

    可以在aop中打印日志啥的

    二.对象类型参数校验

    1.在javaBean中添加注解,对一些特殊字段进行分组,如id,插入数据时,不需要校验可以为null,而查询,删除,更新等操作必须校验

     1 @NotNull(message = "uid不能为null",groups = {Query.class,Update.class,Delete.class})
     2 @Min(value = 1,message = "uid不合法")
     3 private Integer uid;
     4 
     5 @NotBlank(message = "姓名不能为空")
     6 @Size(max = 20,message = "姓名最大长度为50个字符")
     7 private String userName;
     8 
     9 @NotBlank(message = "性别不能为空")
    10 @Size(max = 20,message = "性别最大长度位20个字符")
    11 private String sex;
    12 
    13 @NotNull
    14 @Max(value = 70,message = "最大年龄为70岁")
    15 private Integer age;
    16 
    17 @NotNull
    18 private Integer departmentId;
    19 
    20 @Value(value = "1")
    21 private Integer state;

    分组只是个标记,用接口定义就好

    1 public interface Query {
    2 
    3 }

    2.在参数前添加@Valited注解,该注解支持分组,@Valid不支持,如果你选择的校验位置与上面定义的aop拦截的位置相同,那就会出问题了,

    你的代码会走aop的逻辑然后去用你校验单个参数的校验器去进行校验,这样无法校验出问题,因此推荐放在controller层

    1 @RequestMapping("/insertUser")
    2 public @ResponseBody String insertUser(@Validated @RequestBody User user) {
    3     Integer count = userService.insertUser(user);
    4     return count ==1 ? "成功增加1条记录" : "增加" + user + "失败";
    5 }

    校验指定分组

    3.抛出的异常会走上面贴出的全局异常处理器的代码

    4.测试

    1)正常情况,注意没有id

     2)丢失userName

  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/tele-share/p/11528741.html
Copyright © 2020-2023  润新知