@valid和自定义异常
问题的产生:
当有很多参数需要校验时,比如name,age,email等很多参数都需要判空,或者有长度限制时,如果后端写很多if-else就有很多代码,不美观,不优雅.前端每个参数都效验的话工作量也很大
本文旨在解决这个问题,本文使用@valid 注解来解决这个问题.
首先定义一个
统一结果返回
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Result<T> {
private String msg;
private Integer code;
private T data;
private static final Integer successCode = 200;
private static Integer errorCode = 500;
private static final String successMsg = "成功";
private static final Object resultNoData = null;
public static Result successNoResult() {
return new Result(successMsg, successCode, resultNoData);
}
public static <T> Result<T> successWithResult(T data) {
return new Result(successMsg, successCode, data);
}
public static Result successWithCodeAndMsg(Integer code, String msg) {
return new Result(msg, code, resultNoData);
}
public static Result errorNoResult(String msg) {
return new Result(msg, errorCode, resultNoData);
}
public static Result errorWithResult(String msg, Object data) {
return new Result(msg, errorCode, data);
}
public static Result errorWithCodeAndMsg(Integer code, String msg) {
return new Result(msg, code, resultNoData);
}
}
@valid 注解简单使用
先看下简单使用,复杂的自己查api吧
首先在控制层的参数上加上该注解
import javax.validation.Valid;
import java.util.*;
@RestController
@RequestMapping("/test")
public class Test2 {
@RequestMapping("test2")
public Result<User> test2(@Valid User user){
return Result.successWithResult(user);
}
}
然后在实体类中加上如下注解
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
/**
* @NotEmpty:不能为null,而且长度必须大于0
* @NotBlank:只用在String上,表示传进来的值不能为null,而且调用trim()后,长度必须大于0
* @NotNull:不能为null,但可以为empty
* @Length(message = "名称不能超过个 {max} 字符", max = 10)
* @Range(message = "年龄范围为 {min} 到 {max} 之间", min = 1, max = 100)
*/
@NotNull( message = "ID不能为空")
private Integer id;
@NotBlank( message = "昵称不能为空")
@Size( min = 2,max = 5,message ="昵称的字数个数必须在0和5之间" )
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
请求该接口
http://localhost:8080/test/test2?id=5&name=xxwwwww
查看效果,如下
可以看到,已经能表示该请求参数错误
但是还有个问题,我们不能直接这样子直接返回给前端,需要返回统一的结果
全局异常处理
Spring-boot对于异常的处理也做了不错的支持,
它提供了一个 @ControllerAdvice注解以及 @ExceptionHandler注解,
前者是用来开启全局的异常捕获,后者则是说明捕获哪些异常,对那些异常进行处理。如下
自定义异常
import lombok.Data;
@Data
public class DefinitionException extends RuntimeException {
private Integer errorCode;
private String errorMsg;
public DefinitionException(){
}
public DefinitionException(Integer errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
}
}
异常处理
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(value = DefinitionException.class)
@ResponseBody
public Result bizExceptionHandler(DefinitionException definitionException) {
Result result=new Result();
result.setCode(definitionException.getErrorCode());
result.setMsg(definitionException.getErrorMsg());
return result;
}
/**
* 处理异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result runtimeExceptionHandler(Exception exception) {
Result result=new Result();
result.setCode(500);
result.setMsg(exception.getMessage());
return result;
}
}
测试代码
@RequestMapping("test3")
public Result<String> test3(){
int i=1/0;
return Result.successWithResult("test3");
}
@RequestMapping("test4")
public Result<String> test4(){
throw new DefinitionException(500,"啊哦,报错了");
}
查看结果
将@valid注解抛的异常也返回统一格式
我们再请求一下这个接口
http://localhost:8080/test/test2?id=5&name=xxwwwww
看下结果
返回格式是统一的格式了,但是返回的信息不太友好,我们看看怎么优化
debug一下,看看这个是什么异常
我们看到,这个异常是org.springframework.validation.BindException类的,
我们看下这个类的具体内容,我们只要我们想要的信息就行
这里,我们只要这个信息就可以了我们改动后如下
/**
* 处理异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result runtimeExceptionHandler(Exception exception) {
Result result=new Result();
if(exception instanceof BindException){//注解类异常
StringBuilder sb = new StringBuilder();
BindException bindException= (BindException) exception;
BindingResult bindingResult = bindException.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError item : allErrors) {
sb
.append(item.getDefaultMessage())
.append(',');
}
sb.deleteCharAt(sb.length()-1);
result.setCode(500);
result.setMsg(sb.toString());
}
return result;
}
再请求该接口,得到结果
到此为止,我们已经得到了我们想要的结果
优化代码
最后,我们在优化一下全局异常处理代码如下
import com.yoocar.util.Result;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.List;
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理自定义异常
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result exceptionHandler(Exception exception) {
Result result=new Result();
//自定义类型异常
if(exception instanceof DefinitionException){
DefinitionException definitionException= (DefinitionException) exception;
result.setCode(definitionException.getErrorCode());
result.setMsg(definitionException.getErrorMsg());
}else if(exception instanceof BindException){//@valid注解抛出的异常
//使用StringBuilder来拼接错误信息,减少对象开销
StringBuilder stringBuilder = new StringBuilder();
//获取并拼接所有错误信息
BindException bindException= (BindException) exception;
BindingResult bindingResult = bindException.getBindingResult();
List<ObjectError> allErrors = bindingResult.getAllErrors();
for (ObjectError item : allErrors) {
stringBuilder.append(item.getDefaultMessage())
.append(',');
}
//删除最后一个逗号
stringBuilder.deleteCharAt(stringBuilder.length()-1);
result.setCode(600);//这里自定义了600用于表示参数有误
result.setMsg(stringBuilder.toString());
}else {//其他异常
result.setCode(500);
result.setMsg(exception.getMessage());
}
return result;
}
}
至此,本文结束.文章中若有错误和疏漏之处,还请各位大佬不吝指出,谢谢大家!