เลิกครอบ try-catch ใน Controller จัดการ Error ให้เป็นมาตรฐานด้วย ProblemDetail (Spring Boot 3+)

February 04, 2026 · 2 min read · 1 views

คุณเคยเขียน Controller ที่เต็มไปด้วย try-catch เพื่อดัก Error แล้วปั้น Response กลับไปหา User ไหม?

Code ที่ Dev ส่วนใหญ่เหนื่อยที่จะเขียน:

java
@GetMapping("/users/{id}")
public ResponseEntity<?> getUser(@PathVariable Long id) {
    try {
        User user = userService.findById(id);
        return ResponseEntity.ok(user);
    } catch (UserNotFoundException e) {
        // ต้องมานั่งปั้น Error Object เองทุกที่
        return ResponseEntity.status(404)
                .body(new MyErrorDto("USER_NOT_FOUND", e.getMessage()));
    } catch (Exception e) {
        return ResponseEntity.status(500)
                .body(new MyErrorDto("INTERNAL_ERROR", "Something went wrong"));
    }
}

ปัญหาคืออะไร? (The Impact)

  1. Code รก: Controller ควรมีหน้าที่แค่รับ Request และส่งต่อ Service ไม่ใช่ที่รวม Logic การจัดการ Error
  2. ไม่เสถียร (Inconsistent): บาง endpoint ส่ง error หน้าตาแบบ { message: ... } บางอันส่ง { error_code: ... } Frontend ปวดหัวแน่นอน
  3. เหนื่อย: ต้องเขียน try-catch ซ้ำๆ ทุก Method เป็นร้อยๆ จุด

ทางออก: @ControllerAdvice ผสานร่าง ProblemDetail

ใน Spring Boot 3 มีของเล่นใหม่คือ ProblemDetail ซึ่งเป็นมาตรฐานสากล (RFC 7807) สำหรับการส่ง Error API โดยเฉพาะ เราแค่ประกาศตัวดักจับ (Handler) ไว้ที่เดียว แล้ว Controller ของเราจะเหลือแค่ "Happy Path"

1. Controller จะสะอาดวิ้ง:

java
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
    // เรียก Service ตรงๆ ไม่ต้องสน try-catch
    // ถ้าไม่เจอ User เดี๋ยว Service throw Exception ออกมาเอง
    return userService.findById(id);
}

2. สร้าง Global Exception Handler:

java
@RestControllerAdvice // บอก Spring ว่า class นี้คอยดัก Error จากทุก Controller
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class) // ดักเฉพาะ Error นี้
    public ProblemDetail handleUserNotFound(UserNotFoundException e) {
        
        // Spring Boot 3 สร้าง Object มาตรฐานให้เลย
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.NOT_FOUND, 
            e.getMessage()
        );
        
        problem.setTitle("User Not Found");
        problem.setProperty("customCode", "ERR-001"); // เพิ่ม field พิเศษได้
        
        return problem;
    }
}

ผลลัพธ์ JSON ที่ได้ (มาตรฐาน RFC 7807):

json
{
  "type": "about:blank",
  "title": "User Not Found",
  "status": 404,
  "detail": "User with id 123 does not exist",
  "instance": "/users/123",
  "customCode": "ERR-001"
}

ความแตกต่างของผลลัพธ์ที่ได้

  1. Clean Code 100%: Controller ของคุณจะเหลือบรรทัดเดียว อ่านง่าย สบายตา
  2. Standardization: ทุก Error ในระบบจะมีโครงสร้าง JSON เหมือนกันหมด (Frontend ยิ้มแก้มปริ เขียนตัวดัก Error ที่เดียวจบ)
  3. Separation of Concerns: แยก Logic การทำงาน (Service) ออกจาก Logic การจัดการ Error (Advice) อย่างชัดเจน

บทสรุป

เลิกกลัว Exception จนต้องรีบ catch มันไว้ทุกที่ครับ ปล่อยให้มัน throw ออกมา แล้วใช้ @RestControllerAdvice ดักจัดการทีเดียว นี่คือวิธีที่ทำให้ Code Base ขนาดใหญ่ยังคงดูสะอาดและดูแลรักษาง่ายครับ

Responses (0)

No responses yet. Be the first to respond.