Chuyển tới nội dung chính

Xử lý lỗi tập trung

Chuẩn hoá lỗi cho ứng dụng web: mọi exception được chuyển thành JSON thống nhất (code, message, path, errors), mapping sẵn cho lỗi validation và lỗi bảo mật, kèm điểm mở rộng để thêm handler/customizer theo nghiệp vụ.

Khi nào sử dụng

  • Service cần format lỗi trả về client thống nhất giữa các API.
  • Muốn giảm boilerplate try/catch/@ExceptionHandler thủ công trong từng controller.
  • Cần mapping lỗi validation (@Valid, @NotBlank, @Size...) và lỗi bảo mật (401/403) sang mã lỗi nghiệp vụ.
  • Cần tuỳ chỉnh log lỗi theo HTTP status, ẩn message nhạy cảm, hoặc bổ sung field vào response lỗi (traceId...).

Cài đặt

Thêm dependency vào pom.xml, không cần khai báo version vì BOM đã quản lý:

<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-error</artifactId>
</dependency>

Hướng dẫn khai báo parent/BOM và Maven repository xem tại Cài đặt.

Cấu hình

Dependency có hai auto-configuration độc lập: servlet dùng prefix govex.error.handling.*, reactive dùng prefix govex.error.*. Cả hai đều matchIfMissing = true nên mặc định bật ngay khi thêm dependency.

PropertyMô tảMặc định
govex.error.handling.enabledBật/tắt xử lý lỗi tập trung cho ứng dụng servlettrue
govex.error.enabledBật/tắt xử lý lỗi tập trung cho ứng dụng WebFluxtrue
govex.error.handling.add-path-to-errorThêm đường dẫn request vào thông tin lỗi trả vềtrue
govex.error.handling.search-super-class-hierarchyTìm mapping theo super class khi không có mapping trực tiếpfalse
govex.error.handling.http-status-in-json-responseGhi HTTP status vào JSON response lỗifalse
govex.error.handling.exception-loggingMức ghi log exception: NO_LOGGING, MESSAGE_ONLY, WITH_STACKTRACEWITH_STACKTRACE
govex.error.handling.full-stacktrace-classesDanh sách class exception luôn ghi log kèm stacktrace đầy đủ
govex.error.handling.full-stacktrace-http-statusesDanh sách HTTP status ghi log kèm stacktrace đầy đủ500
govex.error.handling.hide-message-http-statusesDanh sách HTTP status ẩn message lỗi khỏi response
govex.error.handling.log-levelsMức log theo HTTP status, hỗ trợ wildcard dạng 40x, 4xx
govex.error.handling.http-statusesMapping exception class sang HTTP status
govex.error.handling.codesMapping exception class sang mã lỗiBộ defaults có sẵn
govex.error.handling.messagesMapping exception class sang thông điệpBộ defaults có sẵn
govex.error.handling.json-field-names.codeTên field JSON chứa mã lỗicode
govex.error.handling.json-field-names.messageTên field JSON chứa thông điệp lỗimessage
govex.error.handling.json-field-names.errorsTên field JSON chứa danh sách lỗi chi tiếterrors

Dependency nạp sẵn file defaults cho lỗi validation/bảo mật (ví dụ lỗi @NotNull có mã 00400015, lỗi validate chung 00400000); giá trị khai báo trong application.yml ghi đè defaults.

Sử dụng

Ví dụ cấu hình

govex:
error:
handling:
enabled: true
add-path-to-error: true
exception-logging: WITH_STACKTRACE
hide-message-http-statuses:
- "500"
log-levels:
"500": ERROR
"4xx": WARN

Ném lỗi nghiệp vụ

Dùng ServiceException kèm ResultCode để trả đúng mã lỗi/thông điệp:

throw new ServiceException(ResultCode.of("I0010001", "Không tìm thấy hồ sơ"));

Hoặc gắn mã lỗi cố định cho một class exception:

@ResponseErrorCode("I0010001")
public class InvoiceNotFoundException extends ServiceException {

public InvoiceNotFoundException(String invoiceNo) {
super("Không tìm thấy hoá đơn " + invoiceNo);
}
}

Thêm handler cho exception riêng

Khai báo bean ResponseExceptionHandler — các handler được duyệt theo thứ tự, handler đầu tiên canHandle được exception sẽ được dùng:

@Bean
public ResponseExceptionHandler myBusinessExceptionHandler() {
return new ResponseExceptionHandler() {

@Override
public boolean canHandle(Throwable exception) {
return exception instanceof MyBusinessException;
}

@Override
public ErrorResponse handle(Throwable exception) {
return new ErrorResponse(HttpStatus.BAD_REQUEST, "MY_CODE", exception.getMessage());
}
};
}

Bổ sung field vào response lỗi

@Bean
public ErrorResponseCustomizer traceIdCustomizer() {
return response -> response.addErrorProperty("traceId", currentTraceId());
}

Lưu ý

  • Cấu hình servlet và reactive tách biệt: chỉ nhóm property đúng với loại ứng dụng mới có hiệu lực.
  • Các handler/customizer mặc định đều có @ConditionalOnMissingBean — khai báo bean cùng loại để ghi đè.
  • Khi server.error.include-message/include-binding-errors của Spring Boot không bật, danh sách lỗi chi tiết có thể bị lược bỏ hoặc thay bằng thông báo chung ở một số HTTP status.
  • Lỗi validation và lỗi Spring Security chỉ được map sẵn khi class tương ứng (spring-boot-starter-validation, spring-boot-starter-security) có trên classpath.
  • Nên khai báo hide-message-http-statuses cho các status 5xx để không lộ chi tiết kỹ thuật ra ngoài.