- Exception
- 如果在request processing的時候出現exception,則outcome依然是Servlet response。
- exception需要以某種方式被轉換成response
- Spring提供多種方式將exception轉換為response
- 特定的Spring exception將會自動mapping成指定的HTTP status code
- Exception上可以加@ResponseStatus annotation,從而將其mapping為某一個HTTP status code
- method上可以加上@ExceptionHandler,使其用來處理exception
- 將exceptions mapping至HTTP status codes
- default情況下,Spring會將自身的一些exception轉換成合適的status code
- BindException 400 Bad Request
- ConversionNotSupportedException 500 Internal Server Error
- HttpMediaTypeNotAcceptableException 406 Not Acceptable
- HttpMediaTypeNotSupportedException 415 Unsupported Media Type
- HttpMessageNotReadableException 400 Bad Request
- HttpMessageNotWritableException 500 Internal Server Error
- HttpRequestMethodNotSupportedException 405 Method Not Allowed
- MethodArgumentNotValidException 400 Bad Request
- MissingServletRequestParameterException 400 Bad Request
- MissingServletRequestPartException 400 Bad Request
- NoSuchRequestHandlingMethodException 404 Not Found
- TypeMismatchException 400 Bad Request
- 例如,如果DispatcherServlet無法找到適合處理request的controller method,則會拋出NoSuchRequestHandlingMethodException,最終結果就是產生404 status code response
- Spring提供一個機制,能夠透過@ResponseStatus annotation將exception mapping為HTTP status code
- 範例:
- 如果findOne返回null,就會throw SpittleNotFoundException exception,且response code為500。實際上,如果出現任何沒有mapping的exception,response都會是500 status code。
- mapping
- SpittleNotFoundException是一種資源沒找到的異常,其實HTTP404是比較精確的response status code。所以,要使用@ResponseStatus將SpittleNotFoundException mapping至HTTP404
- Exception handling method
- 如果想要response不僅包含status code,還要包含產生的錯誤,此時就不能將exception視為HTTP error,而是要按照處理request的方式來處理exception
- 假設使用者新增的Spittle和已存在的Spittle相同,SpittleRepository的save()將會拋出DuplicateSpittleException
- 這表示SpittleController的saveSpittle() method需要處理這個exception
- 如果能夠讓saveSpittle() method只關注在正確的path,而讓其他method處理exception,也許能夠簡單一點
- 先將saveSpittle() method的exception handler移除(易理解及測試,因為只有一個path)
- 為SpittleController添加一新的method,會處理DuplicateSpittleException的情況,這和處理request是一致的
- 對於@ExceptionHandler來說,他能夠處理同一個controller所有method拋出的DuplicateSpittleException exception,而不需要在每一個可能拋出DuplicateSpittleException的method中增加handle code
- Advising controllers
- 如果controller class特定的aspect能夠運用到整個application的所有controller,這會很方便
- 如果多個controller都會拋出某個特定的exception,可能會發現要在所有controller重複相同的@ExceptionHandler
- 為了避免重複,可以create一個base controller,所有controller都extends這個class,從而extends common @ExceptionHandler
- Spring3.2 introduce一個新的解決方案: controller advice,是任意帶有@ControllerAdvice的class,這個class會包含一個或多個下面annotation的method,且會運用到整個application中的所有controller中帶有@RequestMapping的method上
- @ExceptionHandler
- @InitBinder
- @ModelAttribute
- @ControllerAdvice本身已經使用了@Component,因此@ControllerAdvice所標註的class將會自動被ComponentScanner掃描到,就像帶有@Component一樣
- @ControllerAdvice主要用法:
- 將所有的@ExceptionHandler method蒐集到一個class中,如此一來所有的exception就能在同一個地方進行一致的處理
- 如此一來,任意的controller method如果拋出了DuplicateSpittleException,都會使用duplicateSpittleHandler()來處理Exception
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET) public String spittle(@PathVariable("spittleId") long spittleId, Model model) { Spittle spittle = spittleRepository.findOne(spittleId); if (spittle == null) { throw new SpittleNotFoundException(); } model.addAttribute(spittle); return "spittle"; }
package spittr.web; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found") //將exception mapping至404
public class SpittleNotFoundException extends RuntimeException { }
@RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model) { try { spittleRepository.save( new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; } catch (DuplicateSpittleException e) { return "error/duplicate"; } }
@RequestMapping(method=RequestMethod.POST) public String saveSpittle(SpittleForm form, Model model) { spittleRepository.save( new Spittle(null, form.getMessage(), new Date(), form.getLongitude(), form.getLatitude())); return "redirect:/spittles"; }
@ExceptionHandler(DuplicateSpittleException.class) public String handleDuplicateSpittle() { return "error/duplicate"; }
package spittr.web; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class AppWideExceptionHandler { @ExceptionHandler(DuplicateSpittleException.class) public String duplicateSpittleHandler() { return "error/duplicate"; } }
沒有留言:
張貼留言