Spring MVC 全局异常处理及参数效验小记

Spring MVC 全局异常处理及参数效验小记

Scroll Down

emmmm

参数效验和全局异常处理方式很多,以前接触过一些,所以在此记录下个人觉得很方便快捷的实现。

参数效验

在 Spring MVC 中有很多种效验机制,例如实现效验器接口 Validator 等,不过太过麻烦,此处记录使用 Hibernate Validator 的实现来进行效验,使用的 api 是统一的,是 JSR 的,不过采用的具体实现是 Hibernate 的。为快速实现,使用 Spring Boot 进行。

  1. 添加效验框架的依赖,即使用starter
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
  1. 创建实体类 User,注意,需要使用标注!(Java Bean 效验,下面还有另一种情况)
public class User
{
    @NotBlank(message = "email is not blank")
    private String email;

    @NotBlank(message = "passwd is not blank")
    private String passwd;

    //getter和setter省略
}

上面所使用的 @NotBlank 注解是 Java 中的效验包中提供,不过我们采用了Hibernate 的实现!!!此处含义即是不允许对应字段为空白,即长度0。

  1. 然后创建控制器:
@RestController
@RequestMapping(value = "/user")
public class UserController
{
    @GetMapping(value = "/test")
    public String test(@Valid User user, BindingResult result)
    {
        if (result.hasErrors())
        {
            StringBuilder msg= new StringBuilder();
            for (ObjectError error:result.getAllErrors())
            {
                msg.append(error.getDefaultMessage());
                msg.append("<br>");
            }
            return msg.toString();
        }
        return "no error";
    }
}

注意,此处在处理器方法对应的 User 参数对象那里使用了 @Valid 注解,含义是对此对象进行效验,也可采用 @Validated 注解(建议还是这个吧),效果一样,不过后者是Spring 提供的。方法中还有一个 BindingResult 对象,该对象是用于接收参数效验时发生的效验错误,即不满足我们的要求的一些错误信息。
4. 使用 Http Client 进行接口测试,我们如果传入的参数 email 和 passwd 的长度为0,即空白,则会返回对应字段上面的 message 值,如下:

  • 参数全为空的情况:
    validate_1587745598695
  • 参数部分为空:
    validate2_1587745829423
  • 参数没问题:返回了“no error”
    validate3_1587745916964
  1. 注意,类似于 @NotBlank 的注解还有很多,例如 @NotNull 、@Max 、@Min 等等,在 javax.validation.constraints 包下面。
  2. 直接控制器中单一参数效验!!!这是个巨坑,和 bean 效验不同
@Validated       //进行单一参数效验,需要放类上
@Controller
@RequestMapping(value = "/user")
public class LoginController
{
    @GetMapping(value = "/test")
    @ResponseBody
    //@Validated   //不起作用,要放类上
    public PanelResponse test(@NotBlank(message = "email empty") String email,
                              @NotBlank(message = "password empty") String password)
            throws Exception
    {
        return new PanelResponse().name("no error");
    }
}

运行结果图(注意,参数是空的,即为email=&password=):
validated666_1587925807214
7. List集合的参数效验!!(更新):
最近写一个小东西的时候发现了一个问题,在控制器里如果使用 List 集合接受 bean ,那么将无法通过上面的方式效验,如下:

  //控制器方法如下,则不能效验
  public void test(@Validated List<User> users)
  {
    //****
  }

解决办法:

将List集合封装进一个 bean 里面,同时在 bean 中的 List 上使用效验注解 @Valid !!!,具体如下所示:

//封装的bean
@Data
public class OrderDTO
{
    @Positive(message = "必须大于0")
    private int userId;

    @Size(max = 150,message = "描述请在150字以内")
    private String description;

    @Valid   //必须
    private List<DetailsDTO> orderDTOS;
}

//控制器方法,即可效验
@Description(description = "订单生成")
    @PostMapping(value = "/generate")
    public PanelResponse generateOrder(@RequestBody  @Validated OrderDTO orderDTOS)
    {
        orderService.generateOrder(OrderUtils.bulidOrder(orderDTOS));
        return new PanelResponse().success().message("ok").data(null);
    }

注意(关于上面的单一参数效验)

  • @Validate 和 @Valid 进行单一参数效验时,放参数和方法上都是无用的!
  • @Valid 进行单一参数效验时,放类上也没用,无法效验

全局异常处理

全局异常的实现也有很多,此处记录采用 @ControllerAdvice + @ExceptionHandler 的方式,方便快捷,将异常处理放一起。

  • @ControllerAdvice:用于标注一个全局异常处理器。
  • @ExceptionHandler:标注某个异常处理器,可以通过参数指定处理的异常类型。
  1. 首先创建对应的全局异常处理类和方法,使用 @ControllerAdvice 标注,如下:
@ControllerAdvice    //注明为全局异常处理器
public class Global
{
    private static Logger logger= LoggerFactory.getLogger(Global.class);

    @ExceptionHandler(Exception.class)   //注明处理的异常类型
    public void test(Exception e)
    {
        //System.out.println("发生异常:\n"+e.getMessage());
        logger.info("发生异常:\t{}",e.getMessage());
    }
}
  1. 在控制器里直接抛出一个异常:
@GetMapping(value = "/exception")
    public String ex(String ok) throws Exception
    {
        if (ok.length()==0)
        {
            throw new Exception("异常咯");
        }
        return "no";
    }
  1. 使用 Http Client 请求该接口,ok参数为空白即可,如下:
    globalError_1587746500002