Spring MVC优雅处理业务异常

本文中,我会描述如何在应用程序的不同层次,优雅地处理业务异常。

异常定义

BusinessException基类定义如下,注意异常中携带业务错误码,方便前端处理异常:

public class BusinessException extends RuntimeException {
    private final int errorCode;

    public BusinessException(int errorCode) {
        this.errorCode = errorCode;
    }

    public BusinessException(int errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }

    public BusinessException(int errorCode, String message, Throwable cause) {
        super(message, cause);
        this.errorCode = errorCode;
    }

    public BusinessException(int errorCode, Throwable cause) {
        super(cause);
        this.errorCode = errorCode;
    }

    public BusinessException(int errorCode, String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        this.errorCode = errorCode;
    }

    public int getErrorCode() {
        return errorCode;
    }

}

具体的业务异常继承BusinessException,提供具体的错误码,以及注意提供具体的异常信息:

public class BookNotFoundException extends BusinessException {
    public BookNotFoundException(Long id) {
        super(ErrorCodes.BOOK_NOT_FOUND, "book [%s] not found".formatted(id));
    }
}

异常信息DTO

我们需要返回给前端一个DTO,携带具体的错误信息,定义如下:

@Data
public class ApiError {
    private int errorCode;
    private String message;
    private Object detail;

    public ApiError() {}

    public ApiError(int errorCode, String message, Object detail) {
        this.errorCode = errorCode;
        this.message = message;
        this.detail = detail;
    }

    public ApiError(int errorCode, String message) {
        this(errorCode, message, null);
    }
	
	//从业务异常转为DTO
    public static ApiError valueOf(BusinessException e) {
        return new ApiError(e.getErrorCode(), e.getMessage());
    }

}

Service层

Service层仅负责抛出业务异常。
由于Service层应该专注于业务逻辑,不应该出现Http状态码相关的处理逻辑。

例如:

@Service
public class BookService {
    private final Map<Long, Book> bookMap = new ConcurrentHashMap<>();

    public Book getBookById(Long id) throws BookNotFoundException {
        var book = bookMap.get(id);
        if (book == null) {
        	//抛出业务异常,即找不到对应id的Book
            throw new BookNotFoundException(id);
        }
        return book;
    }
}

Controller层

在Controller层,我们需要处理业务异常,返回相应的Http状态码以及异常信息DTO。

例如:

@RequestMapping("/book")
@RestController
public class BookController {
    private final BookService service;

    public BookController(BookService service) {
        this.service = service;
    }

	//正常情况下200,返回数据
    @GetMapping("/{id}")
    public Book getBookById(@PathVariable Long id) {
        return service.getBookById(id);
    }

	//返回404 NOT_FOUND状态码
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(BookNotFoundException.class)
    public ApiError bookNotFound(BookNotFoundException e) {
    	//将异常转为ApiError对象,返回给客户端
        return ApiError.valueOf(e);
    }
}

效果

访问 GET /book/{id} 获取对应id的书本
正常情况:
Spring MVC优雅处理业务异常_第1张图片

异常情况:
Spring MVC优雅处理业务异常_第2张图片

具体代码

具体可以参考github:
https://github.com/superOTAKU/spring-rest-template

你可能感兴趣的:(spring,mvc,java,异常处理)