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.