0
0
Lập trình
Flame Kris
Flame Krisbacodekiller

🌟 Xử Lý Ngoại Lệ Toàn Cầu Hiện Đại Trong Spring Boot

Đăng vào 3 ngày trước

• 5 phút đọc

Xử Lý Ngoại Lệ Toàn Cầu Hiện Đại Trong Spring Boot

Xử lý ngoại lệ là một phần thiết yếu trong việc xây dựng các API mạnh mẽ và thân thiện với người dùng. Với Spring Boot, bạn có thể tập trung xử lý lỗi thông qua một Global Exception Handler và các ngoại lệ tùy chỉnh để đảm bảo mã nguồn sạch và dễ bảo trì. Hướng dẫn này sẽ giúp bạn thiết lập một trình xử lý ngoại lệ toàn cầu hiện đại, tạo ra một ngoại lệ tùy chỉnh và sử dụng chúng trong các controller – tất cả đều dễ hiểu và nhanh chóng thực hiện.

Mục Lục

  1. Tạo Ngoại Lệ Tùy Chỉnh: ResourceNotFoundException
  2. Triển Khai Trình Xử Lý Ngoại Lệ Toàn Cầu
  3. Ví Dụ Về Controller Sử Dụng Ngoại Lệ
  4. Mẫu Phản Hồi Lỗi
  5. Thực Hành Tốt Nhất
  6. Kết Luận

1. Tạo Ngoại Lệ Tùy Chỉnh: ResourceNotFoundException

Một ngoại lệ tùy chỉnh giúp bạn báo hiệu các trường hợp lỗi cụ thể (như tài nguyên bị thiếu) trong logic kinh doanh của bạn.

java Copy
// filepath: src/main/java/com/example/exception/ResourceNotFoundException.java
package com.example.exception;

/**
 * Được ném ra khi một tài nguyên được yêu cầu không tồn tại.
 */
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

2. Triển Khai Trình Xử Lý Ngoại Lệ Toàn Cầu

Tập trung xử lý lỗi cho tất cả các controller. Ví dụ này sử dụng định dạng phản hồi hiện đại và ghi lại lỗi để phục vụ cho việc gỡ lỗi.

java Copy
// filepath: src/main/java/com/example/exception/GlobalExceptionHandler.java
package com.example.exception;

import com.example.utils.ApiResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    // Xử lý ngoại lệ Tài nguyên Không Tìm Thấy
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiResponse<Object>> handleResourceNotFound(ResourceNotFoundException ex) {
        ApiResponse<Object> response = ApiResponse.error(ex.getMessage(), "RESOURCE_NOT_FOUND");
        return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
    }

    // Xử lý lỗi xác thực
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<List<Map<String, String>>>> handleValidation(MethodArgumentNotValidException e) {
        List<Map<String, String>> details = e.getBindingResult()
            .getAllErrors()
            .stream()
            .map(error -> {
                Map<String, String> errorDetail = new HashMap<>();
                errorDetail.put("field", ((org.springframework.validation.FieldError) error).getField());
                errorDetail.put("message", error.getDefaultMessage());
                return errorDetail;
            })
            .collect(Collectors.toList());

        ApiResponse.Meta meta = new ApiResponse.Meta(
            Instant.now(),
            UUID.randomUUID().toString(),
            "1.0"
        );

        ApiResponse<List<Map<String, String>>> response = new ApiResponse<>(
            "fail",
            "Lỗi xác thực!",
            details,
            meta,
            null
        );

        return new ResponseEntity<>(response, HttpStatus.UNPROCESSABLE_ENTITY);
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Object>> handleGenericException(Exception exception) {
        log.error("Internal Server Error: {}", exception.getMessage(), exception);
        ApiResponse<Object> response = ApiResponse.error(exception.getMessage(), "INTERNAL_SERVER_ERROR");
        return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

3. Ví Dụ Về Controller Sử Dụng Ngoại Lệ

Dưới đây là một phương thức controller đơn giản minh họa cách mà ngoại lệ tùy chỉnh và trình xử lý toàn cầu hoạt động cùng nhau.

Lưu ý: Logic dịch vụ được đặt trong controller để minh họa.

Thực Hành Tốt Nhất: Chuyển logic kinh doanh vào lớp dịch vụ để dễ bảo trì.

java Copy
// filepath: src/main/java/com/example/controller/CategoryController.java
package com.example.controller;

import com.example.entity.Category;
import com.example.exception.ResourceNotFoundException;
import com.example.utils.ApiResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/api/v1/categories")
public class CategoryController {

    // Ví dụ về endpoint GET với logic trong dòng cho mục đích minh họa.
    // Thực Hành Tốt Nhất: Chuyển logic này sang lớp dịch vụ!
    @GetMapping("/{id}")
    public ResponseEntity<ApiResponse<Category>> getCategoryById(@PathVariable Long id) {
        List<Category> categories = Arrays.asList(
            new Category(1L, "Trái cây", "Trái cây tươi", true, "/images/fruits.png"),
            new Category(2L, "Rau củ", "Rau xanh", false, "/images/veggies.png")
        );
        Category found = categories.stream()
            .filter(cat -> cat.getId().equals(id))
            .findFirst()
            .orElseThrow(() -> new ResourceNotFoundException("Không tìm thấy danh mục với id: " + id));

        return ResponseEntity.ok(ApiResponse.success(found, "Lấy danh mục thành công"));
    }
}

4. Mẫu Phản Hồi Lỗi

Khi một danh mục không được tìm thấy, API sẽ trả về:

json Copy
{
  "status": "fail",
  "message": "Lỗi xác thực!",
  "data": [
    { "field": "name", "message": "Tên danh mục là bắt buộc" }
  ],
  "meta": {
    "timestamp": "2025-09-17T12:34:56Z",
    "requestId": "b1a2c3d4-e5f6-7890-abcd-ef1234567890",
    "version": "1.0"
  }
}

5. Thực Hành Tốt Nhất

  • Tập trung xử lý ngoại lệ với @RestControllerAdvice.
  • Sử dụng ngoại lệ tùy chỉnh cho các lỗi rõ ràng, theo miền.
  • Giữ logic kinh doanh trong các lớp dịch vụ để có controller sạch.
  • Trả về phản hồi lỗi có cấu trúc để dễ tích hợp với client.
  • Ghi lại lỗi để phục vụ cho việc gỡ lỗi và giám sát.

Kết Luận

Với trình xử lý ngoại lệ toàn cầu và các ngoại lệ tùy chỉnh, API Spring Boot của bạn sẽ dễ duy trì, gỡ lỗi và sử dụng hơn. Bắt đầu với các mẫu trên và điều chỉnh chúng cho dự án của bạn để có một backend hiện đại và chuyên nghiệp!


Nếu bạn thấy hướng dẫn này hữu ích, hãy chia sẻ nó với nhóm của bạn hoặc đánh giá sao cho kho lưu trữ của bạn!

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào