@valid和自定義異常
- 2021 年 4 月 24 日
- 筆記
@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;
}
}
請求該介面
//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註解拋的異常也返回統一格式
我們再請求一下這個介面
//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;
}
}
至此,本文結束.文章中若有錯誤和疏漏之處,還請各位大佬不吝指出,謝謝大家!