Spring MVC中的异常处理
在一个良好的Rest架构的应用中,所有的异常都应该有对应的Http Status Code来表示具体的异常类型,这样可以客户端可以基于对应的Status Code做出最有利于自己的处理。
在Spring MVC中,异常处理机制有3个选项:
基于Exception的,即只处理某个异常 基于Controller的,即处理某个Controller中抛出的异常。 基于Application的,即处理该Application抛出的所有异常
在我之前的文章(http://ningandjiao.iteye.com/blog/1982635)中,搭建了一个基于Spring4.0的Restful服务,下面就来为这个服务添加Error Handling机制,
基于Exception的异常处理
通常情况下,在应用中抛出的未被捕获的异常,最后会以500(HttpStatus.INTERNAL_SERVER_ERROR)返回客户端,但是,有时服务器的异常是由于客户端发送的请求不合规范导致,这时,我们就需要应该400(BAD_REQUEST)的status code。在Spring MVC中,做到这点非常容易,只需要在对应的Exception上加上@ResponseStatus注解即可。栗子:
测试代码:
@Test public void shouldGetStatus404WhenRquestIdBiggerThan10() throws Exception { mockMvc.perform(get("/requests/11") .contentType(MediaType.APPLICATION_JSON) .accept(MediaType.APPLICATION_JSON) .param("userId", "xianlinbox") ) .andExpect(status().isBadRequest()); }
@RequestMapping(value = "/requests/{requestId}", method = RequestMethod.GET) public Request get(@PathVariable int requestId, @RequestParam(value = "userId") String userId) { if (requestId > 10) { throw new InvalidRequestIdException("Request id must less than 10"); } return new Request(userId, requestId, "GET"); } @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Request id must less than 10")public class InvalidRequestIdException extends RuntimeException { public InvalidRequestIdException(String message) { super(message); }}
@Test public void shouldGetStatus404WhenRquestIdBiggerThan10() throws Exception { mockMvc.perform(get("/requests/11") .param("userId", "xianlinbox") ) .andExpect(status().isBadRequest()) .andExpect(content().string("Request id must less than 10")); } @Test public void shouldGetStatus500WhenUnexpectedErrorHappen() throws Exception { mockMvc.perform(get("/requests/100") .param("userId", "xianlinbox") ) .andExpect(status().isInternalServerError()) .andExpect(content().string("Unexpected Server Error")); }
@ExceptionHandler(InvalidRequestIdException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) public String handleInvalidRequestError(InvalidRequestIdException ex) { return ex.getMessage(); } @ExceptionHandler(RuntimeException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public String handleUnexpectedServerError(RuntimeException ex) { return ex.getMessage(); }
@WebAppConfigurationpublic class ApiControllerIntegrationTest { @Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc; @Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); }...
@ControllerAdvicepublic class ApiExceptionHandler { @ExceptionHandler(InvalidRequestIdException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public String handleInvalidRequestError(InvalidRequestIdException ex) { return ex.getMessage(); } @ExceptionHandler(RuntimeException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public String handleUnexpectedServerError(RuntimeException ex) { return ex.getMessage(); }}