22 điểm bởi lemonmint 2025-06-02 | 5 bình luận | Chia sẻ qua WhatsApp

Khi phát triển HTTP API, xử lý lỗi thường là một phần khá phiền toái. Càng có nhiều API và logic nội bộ càng phức tạp thì càng phát sinh khó khăn ở ba khía cạnh.

  • Trả về mã lỗi phù hợp: Với các lập trình viên còn ít kinh nghiệm, việc dùng mã trạng thái HTTP nhất quán trong logic phức tạp là không dễ.
  • Ghi một lượng lớn log kết quả: Việc ghi log ở mọi điểm kết thúc có thể xảy ra khi lỗi phát sinh làm tăng lượng mã và khiến việc quản lý trở nên phức tạp hơn.
  • Truyền tải thông điệp lỗi rõ ràng: Chỉ truyền một thông điệp lỗi đơn giản cho client là chưa đủ để hiểu rõ và xử lý lỗi một cách chính xác.

Cải thiện việc trả về mã lỗi phù hợp

Để giải quyết vấn đề thiếu nhất quán khi dùng mã lỗi, bài viết đề xuất triển khai interface hoặc struct HttpError bao gồm StatusCodeMessage.

  • Giải pháp:
    • Định nghĩa kiểu HttpError: đóng gói mã trạng thái HTTP và thông điệp.
    • Cung cấp hàm helper: dùng các hàm helper trả về mã lỗi cụ thể như httperror.BadRequest("wrong format") để dễ dàng tạo đối tượng lỗi.
  • Ưu điểm:
    • Tận dụng tính năng tự động hoàn thành của IDE để nhập mã lỗi và thông điệp một cách thuận tiện, an toàn.
    • Giảm khả năng phát sinh lỗi so với việc nhập thủ công mã số.
    • Giảm sự phiền toái khi phải kiểm tra từng tài liệu thiết kế đã chuẩn bị sẵn.

Tập trung hóa việc ghi log

Để giảm việc ghi log lặp lại và quản lý logic xử lý lỗi tại một nơi, bài viết đưa ra cách bọc HTTP handler.

  • Giải pháp:
    • Triển khai router tùy chỉnh (chiwrap.Router): bao gồm bên trong một router hiện có như chi.Router và bổ sung logic xử lý lỗi.
    • Bọc handler: các phương thức như Get của router tùy chỉnh nhận HandlerFunc, thực thi nội bộ, và khi có lỗi thì chuyển đến logic xử lý tập trung.
    • Hàm callback lỗi: khi tạo NewRouter, nhận hàm errCallback để gọi callback đó khi lỗi xảy ra, từ đó ghi log tập trung hoặc thực hiện xử lý bổ sung.
  • Ưu điểm:
    • Khi lỗi phát sinh trong logic API, phản hồi sẽ tự động trả về mã lỗi và thông điệp phù hợp.
    • Có thể đăng ký hàm callback để ghi log phù hợp theo từng dịch vụ, giúp việc quản lý log dễ dàng hơn.
    • Giảm trùng lặp mã và cải thiện khả năng bảo trì.

Truyền tải thông điệp lỗi rõ ràng (ứng dụng RFC7807)

Để client có thể hiểu và xử lý lỗi rõ ràng hơn, bài viết đề xuất cách truyền thông điệp lỗi có cấu trúc dựa trên tiêu chuẩn RFC7807.

  • Các thành phần chính của RFC7807:
    • type: URI định danh loại lỗi (ví dụ: https://example.com/errors/validation).
    • title: mô tả ngắn gọn một dòng về lỗi.
    • status: giống với mã trạng thái HTTP.
    • detail: mô tả lỗi chi tiết mà con người có thể đọc được.
    • instance: URI cụ thể nơi xảy ra lỗi (ví dụ: /api/users/abc).
    • extensions: đối tượng JSON chứa thông tin bổ sung (ví dụ: invalid_field, expected_format).
  • Triển khai:
    • Tạo struct RFC7807Error và bao gồm các thành phần chính.

    • Dễ dàng tạo đối tượng lỗi có cấu trúc thông qua mẫu method chaining (WithType(), WithInstance(), WithExtension()).

    • Có thể chuyển RFC7807Error thành HttpError qua phương thức ToHttpError() để liên kết với router tập trung.

    • Client có thể nắm rõ loại lỗi, nguyên nhân, vị trí phát sinh lỗi một cách rõ ràng.

    • Tăng tính nhất quán và hữu ích của phản hồi API, từ đó nâng cao hiệu quả phát triển phía client.

5 bình luận

 
aer0700 2025-06-02

Cảm ơn bạn vì bài viết hay.

 
beoks 2025-06-02

Cảm ơn vì bài viết hay!
Xin lưu ý là trong Spring có sẵn implementation tại thư viện spirng-web > org.springframework.http.ProblemDetail!

 
honglu 2025-06-02

Cảm ơn vì phần giới thiệu rất hay!
Tôi tìm thử thì thấy nó đã được thay thế bằng RFC 9457.

https://datatracker.ietf.org/doc/html/rfc9457
(Tài liệu 7807 trước đó: https://datatracker.ietf.org/doc/html/rfc7807)

 
findnamo 2025-06-02

Những khác biệt chính giữa RFC 7807 và RFC 9457

  • Quản lý loại vấn đề: 7807 chỉ cho phép dùng URI tùy chỉnh, 9457 giới thiệu registry dùng chung của IANA
  • Xử lý nhiều lỗi: 7807 khuyến nghị dùng mã trạng thái HTTP 207, 9457 nhóm các lỗi liên quan bằng cách dùng mảng errors trong một loại vấn đề duy nhất
  • Trường mở rộng: 7807 cho phép thêm trường tùy ý, 9457 liên kết tường minh các trường được kỳ vọng theo từng loại vấn đề
  • Khuyến nghị bảo mật: 7807 không có, 9457 bổ sung hướng dẫn rõ ràng để ngăn ngừa lỗ hổng bảo mật
  • JSON Pointer: 7807 không hỗ trợ, 9457 chính thức hỗ trợ trường pointer

Đối với các dự án mới sau tháng 7 năm 2023, khuyến nghị áp dụng RFC 9457

 
honglu 2025-06-02

Có vẻ như nên đặt trường type thành một URI có thể dereference được.

Với các dịch vụ nội bộ, có lẽ cũng có thể thay thế bằng liên kết đến tài liệu Swagger UI.