ความยากของการจัดการข้อผิดพลาดใน HTTP API และ RFC7807
(gosuda.org)เมื่อพัฒนา HTTP API การจัดการข้อผิดพลาดมักเป็นส่วนที่ยุ่งยากอยู่เสมอ ยิ่งจำนวน API มากขึ้นและตรรกะภายในซับซ้อนขึ้น ก็จะเกิดความยากในสามด้านดังนี้
- การส่งคืนรหัสข้อผิดพลาดที่เหมาะสม: สำหรับนักพัฒนาที่ประสบการณ์ยังไม่มาก การใช้ HTTP status code อย่างสม่ำเสมอในตรรกะที่ซับซ้อนเป็นเรื่องยาก
- การเขียนล็อกผลลัพธ์จำนวนมาก: เมื่อเกิดข้อผิดพลาด การบันทึกล็อกในทุกจุดสิ้นสุดที่คาดว่าจะเกิดขึ้นจะเพิ่มปริมาณโค้ดและทำให้การดูแลจัดการซับซ้อนขึ้น
- การส่งข้อความข้อผิดพลาดที่ชัดเจน: การส่งเพียงข้อความข้อผิดพลาดให้ไคลเอนต์อย่างเดียวทำให้เข้าใจและจัดการข้อผิดพลาดได้ไม่ชัดเจนนัก
ปรับปรุงการส่งคืนรหัสข้อผิดพลาดที่เหมาะสม
เพื่อแก้ปัญหาความสม่ำเสมอในการใช้รหัสข้อผิดพลาด มีการเสนอวิธีสร้างอินเทอร์เฟซหรือสตรักต์ HttpError ที่มี StatusCode และ Message อยู่ภายใน
- แนวทางแก้ไข:
- นิยามชนิด
HttpError: ครอบรหัสสถานะ HTTP และข้อความไว้ในที่เดียว - จัดเตรียมฟังก์ชัน helper: ใช้ helper function ที่คืนรหัสข้อผิดพลาดเฉพาะ เช่น
httperror.BadRequest("wrong format")เพื่อสร้างอ็อบเจ็กต์ข้อผิดพลาดได้ง่าย
- นิยามชนิด
- ข้อดี:
- ใช้ประโยชน์จากระบบเติมโค้ดอัตโนมัติของ IDE เพื่อกรอกรหัสข้อผิดพลาดและข้อความได้สะดวกและปลอดภัย
- ลดโอกาสเกิดความผิดพลาดเมื่อเทียบกับการกรอกโค้ดตัวเลขด้วยตนเอง
- ลดความยุ่งยากในการต้องเปิดดูเอกสารออกแบบที่เตรียมไว้ล่วงหน้าทีละรายการ
รวมศูนย์การเขียนล็อก
เพื่อลดการเขียนล็อกซ้ำ ๆ และจัดการตรรกะการจัดการข้อผิดพลาดในจุดเดียว จึงเสนอวิธีห่อหุ้ม HTTP handler
- แนวทางแก้ไข:
- สร้างเราเตอร์แบบกำหนดเอง (
chiwrap.Router): ภายในบรรจุเราเตอร์เดิมอย่างchi.Routerและเพิ่มตรรกะจัดการข้อผิดพลาดเข้าไป - ห่อหุ้ม handler: เมธอดอย่าง
Getของเราเตอร์แบบกำหนดเองจะรับHandlerFuncมาทำงานภายใน และเมื่อเกิดข้อผิดพลาดก็จะส่งต่อไปยังตรรกะจัดการส่วนกลาง - ฟังก์ชัน callback สำหรับข้อผิดพลาด: ตอนสร้าง
NewRouterให้รับฟังก์ชันerrCallbackเพื่อเรียกใช้เมื่อเกิดข้อผิดพลาด ทำให้สามารถเขียนล็อกจากส่วนกลางหรือทำงานเพิ่มเติมได้
- สร้างเราเตอร์แบบกำหนดเอง (
- ข้อดี:
- เมื่อเกิดข้อผิดพลาดในตรรกะ API ระบบจะส่งคืนรหัสข้อผิดพลาดและข้อความที่เหมาะสมใน response โดยอัตโนมัติ
- ลงทะเบียนฟังก์ชัน callback เพื่อให้บันทึกล็อกที่เหมาะสมตามแต่ละบริการได้ ทำให้จัดการล็อกได้ง่าย
- ลดโค้ดซ้ำและปรับปรุงความสามารถในการบำรุงรักษา
การส่งข้อความข้อผิดพลาดที่ชัดเจน (ใช้ RFC7807)
เพื่อให้ไคลเอนต์เข้าใจและจัดการข้อผิดพลาดได้ชัดเจนยิ่งขึ้น มีการเสนอแนวทางส่งข้อความข้อผิดพลาดแบบมีโครงสร้างโดยอาศัยมาตรฐาน RFC7807
- องค์ประกอบหลักของ RFC7807:
type: URI ที่ใช้ระบุประเภทข้อผิดพลาด (เช่นhttps://example.com/errors/validation)title: คำอธิบายสั้น ๆ หนึ่งบรรทัดเกี่ยวกับข้อผิดพลาดstatus: เหมือนกับ HTTP status codedetail: คำอธิบายรายละเอียดของข้อผิดพลาดที่มนุษย์อ่านเข้าใจได้instance: URI เฉพาะที่เกิดข้อผิดพลาด (เช่น/api/users/abc)extensions: อ็อบเจ็กต์ JSON สำหรับเก็บข้อมูลเพิ่มเติม (เช่นinvalid_field,expected_format)
- การนำไปใช้:
-
สร้างสตรักต์
RFC7807Errorและใส่องค์ประกอบหลักไว้ภายใน -
ใช้แพตเทิร์น method chaining (
WithType(),WithInstance(),WithExtension()) เพื่อสร้างอ็อบเจ็กต์ข้อผิดพลาดแบบมีโครงสร้างได้ง่าย -
สามารถแปลง
RFC7807Errorเป็นHttpErrorผ่านเมธอดToHttpError()เพื่อเชื่อมต่อกับเราเตอร์แบบรวมศูนย์ได้ -
ทำให้ไคลเอนต์สามารถระบุประเภท สาเหตุ และตำแหน่งที่เกิดข้อผิดพลาดได้อย่างชัดเจน
-
เพิ่มความสม่ำเสมอและประโยชน์ใช้สอยของ API response ส่งผลให้การพัฒนาไคลเอนต์มีประสิทธิภาพมากขึ้น
-
5 ความคิดเห็น
ขอบคุณสำหรับบทความดีๆ ครับ
ขอบคุณสำหรับบทความดีๆ ครับ!
ขอเสริมว่าใน Spring มี implementation อยู่ในไลบรารี
spirng-web>org.springframework.http.ProblemDetailครับ!ขอบคุณสำหรับการแนะนำดี ๆ ครับ!
ลองค้นดูแล้ว พบว่าถูกแทนที่ด้วย RFC 9457 แล้วนะครับ
https://datatracker.ietf.org/doc/html/rfc9457
(เอกสาร 7807 เดิม: https://datatracker.ietf.org/doc/html/rfc7807)
ความแตกต่างหลักระหว่าง RFC 7807 และ RFC 9457
errorsภายในประเภทปัญหาเดียวเพื่อจัดกลุ่มข้อผิดพลาดที่เกี่ยวข้องpointerอย่างเป็นทางการสำหรับโปรเจกต์ใหม่ตั้งแต่หลังเดือนกรกฎาคม 2023 แนะนำให้ใช้ RFC 9457
ดูเหมือนว่าจะแนะนำให้ตั้งค่าฟิลด์
typeเป็น URI ที่สามารถ dereference ได้สำหรับบริการภายใน น่าจะใช้ลิงก์เอกสาร Swagger-ui แทนได้โดยไม่มีปัญหา