emmmm
参数效验和全局异常处理方式很多,以前接触过一些,所以在此记录下个人觉得很方便快捷的实现。
参数效验
在 Spring MVC 中有很多种效验机制,例如实现效验器接口 Validator 等,不过太过麻烦,此处记录使用 Hibernate Validator 的实现来进行效验,使用的 api 是统一的,是 JSR 的,不过采用的具体实现是 Hibernate 的。为快速实现,使用 Spring Boot 进行。
- 添加效验框架的依赖,即使用starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
- 创建实体类 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。
- 然后创建控制器:
@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 值,如下:
- 参数全为空的情况:
- 参数部分为空:
- 参数没问题:返回了“no error”
- 注意,类似于 @NotBlank 的注解还有很多,例如 @NotNull 、@Max 、@Min 等等,在 javax.validation.constraints 包下面。
- 直接控制器中单一参数效验!!!这是个巨坑,和 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=):
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:标注某个异常处理器,可以通过参数指定处理的异常类型。
- 首先创建对应的全局异常处理类和方法,使用 @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());
}
}
- 在控制器里直接抛出一个异常:
@GetMapping(value = "/exception")
public String ex(String ok) throws Exception
{
if (ok.length()==0)
{
throw new Exception("异常咯");
}
return "no";
}
- 使用 Http Client 请求该接口,ok参数为空白即可,如下: