Exception Handling with RESTful Web Services part 1/3

Phai Panda
3 min readJul 23, 2019

--

จากโปรเจกต์ Spring Boot with Oracle Database and RESTful Web Services เรามาต่อกันที่การจัดการ Exception ครับ สืบเนื่องจากโค้ดเดิมที่เคยเห็นหรือดูแลอยู่ก็มีเรื่องนี้ให้ขบคิดไม่น้อย

เริ่มที่มือใหม่ก่อน ก่อนจะเอาโปรเจกต์เดิมมาทำเพิ่ม

รู้จัก Java Exception กันไหม

​Java Exception มันคืออะไร เกิดขึ้นเมื่อไร และมีวิธีจัดการอย่างไร?

https://www.protechtraining.com/content/java_fundamentals_tutorial-exceptions

ทฤษฎีของมันมีอยู่ครับ แต่ถ้าจะให้ผมเล่าแบบ pros เนี่ย มันก็จะตัดโน่นนี่ออกเสียมาก มากจนผมแนะนำว่าให้เพื่อนกลับไปอ่านทฤษฎีของมันเองดีกว่า (ขำหนักมาก) แต่ไหนๆเพื่อนก็หลงมาอ่านถึงตรงนี้แล้ว ผมก็คงจะปล่อยให้หนีไปง่ายๆไม่ได้

ดูภาพข้างต้นเป็นสำคัญเลยครับ จำภาพนะ ไม่ต้องจำตัวอักษร อักษรให้เครื่องมือหรือการจดบันทึกหรือฝึกปฏิบัติช่วยแทน

เพื่อนเขียนหรือรู้จักจาวา เพื่อนคงได้ยินคำว่า “ออบเจ็กต์ (Object)” มาบ้าง เจ้านี่แหละคือบรรพบุรุษของสรรพสิ่งในโลกของจาวาโปรแกรมมิ่ง มันต้องเป็นอย่างแรกเสมอที่กำเนิดขึ้นในหน่วยความจำ จากนิยามบอกว่าทุกคลาสคือออบเจ็กต์ สมบัติของออบเจ็กต์นั้นถ่ายทอดเสมอ (ลึกไปตัดกลับมา)

ส่วน Exception ตัวมันเองแปลว่า “ข้อยกเว้น” ไม่ใช่ข้อผิดพลาดนะ ข้อยกเว้นคือไม่ได้ตั้งใจและยังสามารถจัดการได้ ส่วนข้อผิดพลาด (Error) นั่นน่ะจัดการไม่ได้แล้ว ปล่อยตายอย่างเดียว

ทุกออบเจ็กต์มีสิทธิ์ทำงานแล้วพังได้หมด มีสองเวลาใหญ่ๆจากที่เรียนมาคือ 1. ตอน compile และ 2. ตอน runtime หรือทำงานไปแล้ว

อธิบายตอน compile กล่าวคือโปรแกรมแปลภาษาจาวาตรวจจับได้ก่อนแล้วรายงานผลให้เราแก้ไข หากไม่แก้ไขให้ถูกต้อง ก็ไม่มีวันที่มันจะยอมแปลภาษาจาวาให้กลายเป็น Bytecode (ลึกไปกลับมา)

อธิบายตอน runtime คือโปรแกรมทำงานไปแล้วแล้วทำงาน Bytecode นั้นผิดพลาด โป้ง! โปรแกรมพัง อันนี้นานาจิต

ทุกออบเจ็กต์มีสิทธิ์ทำงานแล้วพังได้หมด จากภาพจึงต้องมีระบบการจัดการความพัง (ฟังแล้วตลก) เมื่อพังจึงตะโกนออกมาอย่างดัง เฮีย~! (ไม่ใช่แระ) คือโยนสิ่งที่เรียกว่า Throwable ออกมา คำว่า able นี้แปลว่า สามารถ ส่วน Throw แปลว่า โยน รวมกันคือ สามารถโยน (แปลทำไมเนี่ย) Throwable เป็นได้อีกสองอย่าง (จาวาเรียกว่าสืบทอด) 1.Error 2.Exception

Error ในรูปประกอบด้วย

  • OutOfMemoryError
  • StackOverflowError
  • LinkageError

ฟาก Error นี้ปล่อยตายครับ เพราะเป็นเรื่องของ Java Virtual Machine (JVM) คือมันพังเพราะจัดการหน่วยความจำไม่ได้

Exception ในรูปประกอบด้วยหลายสิ่ง เช่น

  • RuntimeException
  • IOException
  • อื่นๆ

ฟาก Exception นี้เวลาพังแล้วจัดการได้ครับ (เพราะเป็นข้อยกเว้นไง) เพื่อนดูให้ดีมันแบ่งออกเป็นสองอย่างใหญ่ๆ 1.เกิดเพราะ CPU (RuntimeException) 2.เกิดเพราะอ่านเขียนด้วยอุปกรณ์ Input & Output (IOException)

อ่านเพิ่มเติม

เมื่อโปรแกรมทำงานแล้วพัง Exception จะถูกโยนออกมา ตัวมันประกอบด้วยรายละเอียดของสิ่งที่เกิดขึ้น (information about the error) ซึ่งเราสามารถจัดการได้ 2 ทาง

  1. รับผิดชอบสิ่งที่เกิดขึ้นด้วย try and catch (ในเมื่อคุณท้อง ผมจะรับเป็นพ่อเอง)
  2. โยนมันต่อไปด้วย throw (แม้คุณจะท้องแต่เด็กคนนี้ไม่ใช่ลูกผม!)

เพื่อนเป็นคนประเภทไหนครับ?

เรื่องของ Java Exception ผมขอกล่าวเพียงเท่านี้ก่อน มีโอกาสได้เขียนบทความต่อๆไปไว้จะมารับใช้ใหม่

Exception Handling with RESTful Web Services

ปกติแล้ว RESTful Web Services ที่เขียนไปนั้นจะให้ผลลัพธ์อย่างหนึ่งที่เรียกว่า HTTP response status code ตอบกลับไปยัง Client แน่นอนว่าเพื่อนได้รู้จักมาแล้ว เช่น

  • 200 OK ทุกอย่างเรียบร้อยดี
  • 404 Not Found ไม่พบสิ่งที่ขอมา
  • 405 Method Not Allowed ไม่มีบริการที่ร้องขอ เช่น ไม่มีบริการ @POST แต่ Client กลับร้องขอ POST
  • 406 Not Acceptable ไม่สามารถจัดการประเภทข้อมูลที่ต้องการนี้ได้ ตัวอย่าง ต้องการ XML ซึ่งยังไม่รองรับ (ให้ได้เฉพาะ JSON)
  • อื่นๆ
https://restfulapi.net/http-status-codes/

จากโปรเจกต์ที่ได้ทำไปแล้วก่อนหน้านี้

เรามาเริ่มกันเลย ดูในฐานข้อมูลก่อน

ตัวอย่างนี้มีข้อมูลคือหนังสือ 2 เล่ม

เอาล่ะ ผมอยากลบหนังสือที่มี ID เท่ากับ 2 มีไหม? ดูในฐานข้อมูลบอกได้ทันที ลบไม่ได้เพราะมันไม่มีอยู่ ทีนี้ไปดูโค้ดจาวาแล้วเก็บไว้ในใจ

@DeleteMapping(value = "/books/{id}")
public void delete(@PathVariable Long id) {
Optional<Book> optional = bookRepository.findById(id);
if(optional.isPresent()) {
bookRepository.delete(optional.get());
}
}

แล้วผมจะส่งคำขอลบจาก Postman ว่าให้ลบหนังสือที่มี ID เท่ากับ 2

DELETE localhost:8080/books/2

ผลลัพธ์

Status: 200 OK

ตกลงลบได้ไหม? คำตอบลบไม่ได้ (เพราะ isPresent มีค่าเป็น false) แต่ยังตอบกลับมาว่า OK เรียบร้อย ได้เหรอ!?

เหตุนี้เราจึงต้องมาประดิษฐ์ HTTP response status code เอง ประโยชน์คือใช้รายงานผลลัพธ์เพื่อตอบไปยัง Client เพื่อที่ Client จะได้ใช้มันดำเนินการต่อไป (เช่น แสดง Dialog แจ้งแก่ User)

ResponseEntity

คลาสนี้เป็นลูกของคลาส HttpEntity ดังนั้นตัวมันรู้จัก HTTP response status code ทั้งหมด เมื่อเราให้ ResponseEntity เป็น return type แล้ว เราก็สามารถเปลี่ยนแปลง status code ดังกล่าวได้ตามความต้องการ

แท้จริง ResponseEntity สามารถจัดการ HTTP response ได้ตามนี้

  • status code
  • headers
  • body

เอาง่ายๆก่อน แก้ไขโค้ดฝั่ง back end เปิดคลาส BookController เมธอด delete

@DeleteMapping(value = "/books/{id}")
public ResponseEntity delete(@PathVariable Long id) {
Optional<Book> optional = bookRepository.findById(id);
if(optional.isPresent()) {
bookRepository.delete(optional.get());
return new ResponseEntity(HttpStatus.OK);
}
return new ResponseEntity(HttpStatus.NOT_FOUND);
}

ส่งคำขอลบหนังสือที่ไม่มีอยู่อีกครั้ง

DELETE localhost:8080/books/2

ผลลัพธ์ต้องบอกว่าหาไม่เจอ (404 Not Found)

มุมขวาล่างเลยครับ Status 404 Not Found สำเร็จ!

แล้วถ้าเกิดว่าลบหนังสือที่มีอยู่จริงๆล่ะ ต้องได้ 200 OK กลับมานะ ไปลองกัน

DELETE localhost:8080/books/1
200 OK เยี่ยม

โอกาสถัดไป part 2/2 เรามาดูกันว่าจะ custom และเล่นกับเรื่อง Exception Handling ของ RESTful Web Services นี้ได้อีกมากน้อยครับ

--

--