Sự khó khăn của việc xử lý lỗi HTTP API và RFC7807
(gosuda.org)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 StatusCode và Message.
- 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.
- Định nghĩa kiểu
- Ư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.Routervà bổ sung logic xử lý lỗi. - Bọc handler: các phương thức như
Getcủa router tùy chỉnh nhậnHandlerFunc, 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àmerrCallbackđể 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.
- Triển khai router tùy chỉnh (
- Ư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
RFC7807Errorvà 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
RFC7807ErrorthànhHttpErrorqua phương thứcToHttpError()để 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
Cảm ơn bạn vì bài viết hay.
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!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)
Những khác biệt chính giữa RFC 7807 và RFC 9457
errorstrong một loại vấn đề duy nhấtpointerĐối với các dự án mới sau tháng 7 năm 2023, khuyến nghị áp dụng RFC 9457
Có vẻ như nên đặt trường
typethà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.