spring boot 統一介面異常返回值
阿新 • • 發佈:2021-10-26
建立業務 Exception
一般在實際專案中,推薦建立自己的 Exception
型別,這樣在後期會更容易處理,也比較方便統一,否則,可能每個人都丟擲自己喜歡的異常型別,而造成程式碼混亂
ServiceException
用於丟擲業務邏輯校驗異常
UnauthorizedException
用於丟擲使用者未登入異常
可根據自己的專案需求變化,但簡單專案中這兩個已經夠用
ServiceException
package cn.myesn.exception; public class ServiceException extends RuntimeException { /** * Constructs a new runtime exception with {@code null} as its * detail message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause}. */ public ServiceException() { super(); } /** * Constructs a new runtime exception with the specified detail message. * The cause is not initialized, and may subsequently be initialized by a * call to {@link #initCause}. * * @param message the detail message. The detail message is saved for * later retrieval by the {@link #getMessage()} method. */ public ServiceException(String message) { super(message); } /** * Constructs a new runtime exception with the specified detail message and * cause. <p>Note that the detail message associated with * {@code cause} is <i>not</i> automatically incorporated in * this runtime exception's detail message. * * @param message the detail message (which is saved for later retrieval * by the {@link #getMessage()} method). * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A <tt>null</tt> value is * permitted, and indicates that the cause is nonexistent or * unknown.) * @since 1.4 */ public ServiceException(String message, Throwable cause) { super(message, cause); } /** * Constructs a new runtime exception with the specified cause and a * detail message of <tt>(cause==null ? null : cause.toString())</tt> * (which typically contains the class and detail message of * <tt>cause</tt>). This constructor is useful for runtime exceptions * that are little more than wrappers for other throwables. * * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A <tt>null</tt> value is * permitted, and indicates that the cause is nonexistent or * unknown.) * @since 1.4 */ public ServiceException(Throwable cause) { super(cause); } /** * Constructs a new runtime exception with the specified detail * message, cause, suppression enabled or disabled, and writable * stack trace enabled or disabled. * * @param message the detail message. * @param cause the cause. (A {@code null} value is permitted, * and indicates that the cause is nonexistent or unknown.) * @param enableSuppression whether or not suppression is enabled * or disabled * @param writableStackTrace whether or not the stack trace should * be writable * @since 1.7 */ protected ServiceException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
UnauthorizedException
package cn.myesn.exception; public class UnauthorizedException extends RuntimeException { /** * Constructs a new runtime exception with {@code null} as its * detail message. The cause is not initialized, and may subsequently be * initialized by a call to {@link #initCause}. */ public UnauthorizedException() { super(); } /** * Constructs a new runtime exception with the specified detail message. * The cause is not initialized, and may subsequently be initialized by a * call to {@link #initCause}. * * @param message the detail message. The detail message is saved for * later retrieval by the {@link #getMessage()} method. */ public UnauthorizedException(String message) { super(message); } /** * Constructs a new runtime exception with the specified detail message and * cause. <p>Note that the detail message associated with * {@code cause} is <i>not</i> automatically incorporated in * this runtime exception's detail message. * * @param message the detail message (which is saved for later retrieval * by the {@link #getMessage()} method). * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A <tt>null</tt> value is * permitted, and indicates that the cause is nonexistent or * unknown.) * @since 1.4 */ public UnauthorizedException(String message, Throwable cause) { super(message, cause); } /** * Constructs a new runtime exception with the specified cause and a * detail message of <tt>(cause==null ? null : cause.toString())</tt> * (which typically contains the class and detail message of * <tt>cause</tt>). This constructor is useful for runtime exceptions * that are little more than wrappers for other throwables. * * @param cause the cause (which is saved for later retrieval by the * {@link #getCause()} method). (A <tt>null</tt> value is * permitted, and indicates that the cause is nonexistent or * unknown.) * @since 1.4 */ public UnauthorizedException(Throwable cause) { super(cause); } /** * Constructs a new runtime exception with the specified detail * message, cause, suppression enabled or disabled, and writable * stack trace enabled or disabled. * * @param message the detail message. * @param cause the cause. (A {@code null} value is permitted, * and indicates that the cause is nonexistent or unknown.) * @param enableSuppression whether or not suppression is enabled * or disabled * @param writableStackTrace whether or not the stack trace should * be writable * @since 1.7 */ protected UnauthorizedException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
建立異常返回值包裝類
package cn.myesn.exception;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class GlobalExceptionResponseResult {
private String message;
}
建立全域性異常處理者
建立 GlobalExceptionHandler
處理類,該類處理 spring boot rest controller
中丟擲的所有異常
package cn.myesn.handler; import cn.myesn.exception.GlobalExceptionResponseResult; import cn.myesn.exception.ServiceException; import cn.myesn.exception.UnauthorizedException; import org.springframework.http.HttpStatus; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.validation.BindException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class GlobalExceptionHandler { // 根本就沒有傳遞引數時的異常 @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public GlobalExceptionResponseResult handle(HttpMessageNotReadableException e) { return new GlobalExceptionResponseResult().setMessage("引數不能為空"); } // 傳了引數,但沒有通過 validation 時的異常 @ExceptionHandler(BindException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public GlobalExceptionResponseResult handle(BindException e) { final ObjectError objectError = e.getBindingResult().getAllErrors().get(0); return new GlobalExceptionResponseResult().setMessage(objectError.getDefaultMessage()); } // 程式碼中丟擲的邏輯異常 @ExceptionHandler(ServiceException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public GlobalExceptionResponseResult handle(ServiceException e) { return new GlobalExceptionResponseResult().setMessage(e.getMessage()); } // 未登入異常 @ExceptionHandler(UnauthorizedException.class) @ResponseStatus(value = HttpStatus.UNAUTHORIZED) public void handle(UnauthorizedException e) { } // 但以上幾種錯誤都未能匹配到時,catch 一個基礎的異常型別,基本上能捕獲所有該捕獲的異常型別 @ExceptionHandler(RuntimeException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public GlobalExceptionResponseResult handle(RuntimeException e) { return new GlobalExceptionResponseResult().setMessage(String.format("未處理的異常:%s", e.getMessage())); } }
實際使用
新建一個 TestController
檔案,在裡面新增如下端點:
@GetMapping("check-username")
public ResponseEntity<?> checkUsername(@RequestParam String username) {
if (StringUtils.isBlank(username)) {
throw new ServiceException("使用者名稱不能為空");
}
if (userService.existsUserName(username)) {
throw new ServiceException("使用者名稱已存在");
}
return ResponseEntity.ok().build();
}
這樣,寫業務程式碼時,throw
就行了,程式碼整體更符合語義,也更加簡潔明瞭