22 คะแนน โดย lemonmint 2025-06-02 | 5 ความคิดเห็น | แชร์ทาง WhatsApp

เมื่อพัฒนา 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 code
    • detail: คำอธิบายรายละเอียดของข้อผิดพลาดที่มนุษย์อ่านเข้าใจได้
    • instance: URI เฉพาะที่เกิดข้อผิดพลาด (เช่น /api/users/abc)
    • extensions: อ็อบเจ็กต์ JSON สำหรับเก็บข้อมูลเพิ่มเติม (เช่น invalid_field, expected_format)
  • การนำไปใช้:
    • สร้างสตรักต์ RFC7807Error และใส่องค์ประกอบหลักไว้ภายใน

    • ใช้แพตเทิร์น method chaining (WithType(), WithInstance(), WithExtension()) เพื่อสร้างอ็อบเจ็กต์ข้อผิดพลาดแบบมีโครงสร้างได้ง่าย

    • สามารถแปลง RFC7807Error เป็น HttpError ผ่านเมธอด ToHttpError() เพื่อเชื่อมต่อกับเราเตอร์แบบรวมศูนย์ได้

    • ทำให้ไคลเอนต์สามารถระบุประเภท สาเหตุ และตำแหน่งที่เกิดข้อผิดพลาดได้อย่างชัดเจน

    • เพิ่มความสม่ำเสมอและประโยชน์ใช้สอยของ API response ส่งผลให้การพัฒนาไคลเอนต์มีประสิทธิภาพมากขึ้น

5 ความคิดเห็น

 
aer0700 2025-06-02

ขอบคุณสำหรับบทความดีๆ ครับ

 
beoks 2025-06-02

ขอบคุณสำหรับบทความดีๆ ครับ!
ขอเสริมว่าใน Spring มี implementation อยู่ในไลบรารี spirng-web > org.springframework.http.ProblemDetail ครับ!

 
honglu 2025-06-02

ขอบคุณสำหรับการแนะนำดี ๆ ครับ!
ลองค้นดูแล้ว พบว่าถูกแทนที่ด้วย RFC 9457 แล้วนะครับ

https://datatracker.ietf.org/doc/html/rfc9457
(เอกสาร 7807 เดิม: https://datatracker.ietf.org/doc/html/rfc7807)

 
findnamo 2025-06-02

ความแตกต่างหลักระหว่าง RFC 7807 และ RFC 9457

  • การจัดการประเภทปัญหา: 7807 ใช้ได้เฉพาะ URI แบบกำหนดเอง ส่วน 9457 เพิ่ม shared registry ของ IANA
  • การจัดการข้อผิดพลาดหลายรายการ: 7807 แนะนำให้ใช้ HTTP status code 207 ส่วน 9457 ใช้อาร์เรย์ errors ภายในประเภทปัญหาเดียวเพื่อจัดกลุ่มข้อผิดพลาดที่เกี่ยวข้อง
  • ฟิลด์ส่วนขยาย: 7807 สามารถเพิ่มฟิลด์ใดก็ได้ ส่วน 9457 เชื่อมโยงฟิลด์ที่คาดหวังกับแต่ละประเภทปัญหาอย่างชัดเจน
  • คำแนะนำด้านความปลอดภัย: 7807 ไม่มี ส่วน 9457 เพิ่มแนวทางอย่างชัดเจนเพื่อป้องกันช่องโหว่ด้านความปลอดภัย
  • JSON Pointer: 7807 ไม่รองรับ ส่วน 9457 รองรับฟิลด์ pointer อย่างเป็นทางการ

สำหรับโปรเจกต์ใหม่ตั้งแต่หลังเดือนกรกฎาคม 2023 แนะนำให้ใช้ RFC 9457

 
honglu 2025-06-02

ดูเหมือนว่าจะแนะนำให้ตั้งค่าฟิลด์ type เป็น URI ที่สามารถ dereference ได้

สำหรับบริการภายใน น่าจะใช้ลิงก์เอกสาร Swagger-ui แทนได้โดยไม่มีปัญหา