`

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);
    }
}

这个方式有很大的局限性:

1. 只能处理自己写的Exception
2. 不能定制Response的消息体。

基于Controller的异常处理
在每个Controller中,可以定义处理各种异常的方法,在该方法添加@ExceptionHandler定义该方法处理的Exception(所有的Exception都支持),添加@ResponseStatus定义该Exception应该返回的Http Status Code,方法的返回值可以是定制化的异常信息, 这就解决了上面Exception方式的局限性。 栗子:
测试代码:

    @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();
    }


这种处理方式其它都好,就是有个最大的弊端,只能处理一个Controller的异常,对于又多个Controller的情况就会搞出很多的重复代码。

基于Application的异常处理
我个人觉得一个好的异常处理机制应该是这样的,有一个集中的处理点负责所有的异常处理,在真正的业务逻辑的处理过程中,我只会关心正常的业务流程,一旦遇到异常,我只管抛出对应的异常和相关的信息就行了。幸运的是,Spring MVC就提供了这样的机制,开发者可以自己定义一个ExceptionHandler类处理所有的异常,在这个类上加上@ControllerAdvice注解,这个类就以AOP的形式注册到SpringMVC的处理链条中了,在应用任何地方抛出Exception,最后都会调用到这个handler中的方法上,在Handler类中,可以和上面一样使用@ExceptionHandler注解来区别对待不同的Exception。栗子:

测试代码:
同上:但是需要注意一点,使用@ControllerAdvice是以AOP的形式做了一个切面处理异常,因此,你必须模拟整个Web处理过程该注解才能起效。因此在Integration测试类中,你需要加上@WebAppConfiguration注解,同时使用WebApplicationContext构建MockMvc,使用基于Controller的standaloneSetup是不能达到想要的效果的。
@WebAppConfiguration
public class ApiControllerIntegrationTest {
    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }
...

实现代码:
@ControllerAdvice
public 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();
    }
}

4
1
分享到:
评论
6 楼 sqz10200 2016-04-14  
博主写的不错,  思想和写的代码都挺好   我平时都是看老外的写的例子,  你这个写的确实不错. 我原本是想把web.xml中的error-page这快内容放到java代码内实现的,  我想去掉web.xml内所有的配置.  之前在google上查过找到http://stackoverflow.com/questions/10813993/using-spring-mvc-3-1-webapplicationinitializer-to-programatically-configure-ses
老外的这片文章和你的解决方法是一样的.  我这里有一个问题  对于这个error是基于spring的aop去实现的.   那么和之前写在web.xml的有什么区别呢?  其实这个注解也应该是基于原生的做的扩展吧...如果我不想用spring 方式  而是基于WebApplicationInitializer  这个是不是也可以实现呢?
5 楼 whhwkm 2016-01-17  
好文来顶,nice
4 楼 ningandjin 2014-10-17  
sulibra 写道
不用再配置文件里写些什么吗?

你的spring配置文件中需要添加annotation-driven的配置,告诉Spring容器根据Annotation加载。。
3 楼 sulibra 2014-10-16  
不用再配置文件里写些什么吗?
2 楼 ningandjin 2014-03-30  
sachxp 写道
非常感谢楼主,写的非常详细,很棒。拜读之后收益颇丰。

基于Controller的异常处理那段示例代码里漏了 @ResponseBody annotation,会导致Response无视 @ResponseStatus 的设置永远返回400错误。

查了一会儿才发现问题。


有用就好,我的例子是基于Spring4.0的,在Controller上使用的annotation是@RestController而不是@Controller, @RestController和@Controller的区别就是会为Controller的每个@RequestMapping方法加上@ResponseBody的annotation。
1 楼 sachxp 2014-03-27  
非常感谢楼主,写的非常详细,很棒。拜读之后收益颇丰。

基于Controller的异常处理那段示例代码里漏了 @ResponseBody annotation,会导致Response无视 @ResponseStatus 的设置永远返回400错误。

查了一会儿才发现问题。

相关推荐

    spring mvc异常处理

    在异常处理中,记录详细的错误信息至关重要。你可以使用`Logger`对象来记录异常堆栈跟踪和其他相关信息,以便于后续的调试和问题定位。 7. **异常转换** 有时,你可能希望将特定的异常转换为其他类型,以便更好地...

    spring mvc统一处理异常

    spring mvc统一处理异常,通过@ControllerAdvice+@ExceptionHandler

    Spring MVC中异常处理的三种方式

    Spring MVC 中异常处理的三种方式 Spring MVC 框架为我们提供了三种方式来处理异常,分别是实现 HandlerExceptionResolver、使用 @ControllerAdvice 和 @ExceptionHandler 注解、使用 ErrorController。下面我们将...

    spring mvc异常简单处理

    在Spring MVC框架中,异常处理是一项关键任务,它确保了应用程序在遇到错误或异常时能够优雅地响应。本文将深入探讨Spring MVC中的异常处理机制,包括如何配置、自定义异常处理器以及异常转换策略。 首先,Spring ...

    Spring MVC 教程快速入门 深入分析

    十一、Spring MVC如何实现全局的异常处理:提供了实现全局异常处理器的方式,让开发者能够捕获所有控制器抛出的异常,统一处理。 十二、Spring MVC如何把全局异常记录到日志中:讲述了如何将异常信息记录到日志文件...

    最全最经典spring-mvc教程

    错误处理和异常处理也是Spring MVC中的重要部分,通过@ControllerAdvice和@ExceptionHandler可以全局处理异常,提供统一的错误页面。 最后,测试是任何应用程序开发的重要环节。Spring MVC提供了MockMVC,可以在不...

    Spring MVC 4.2.3

    6. **异常处理**:通过定义全局异常处理器,可以统一处理应用程序中的异常,提高代码的整洁性和可维护性。 7. **多视图解析器**:Spring MVC支持多种视图解析器,如JSP、FreeMarker、Thymeleaf等,可以根据项目需求...

    Spring.MVC-A.Tutorial-Spring.MVC学习指南 高清可复制版PDF

    除此之外,Spring MVC还支持数据绑定、验证、本地化、主题、异常处理等功能。例如,使用@ModelAttribute注解可以将请求参数绑定到Controller方法的参数上,@Valid用于进行数据验证,Validator接口可以自定义验证逻辑...

    Spring MVC 4.2.4.RELEASE 中文文档

    异常处理** 通过自定义异常处理器,Spring MVC允许优雅地处理运行时异常,提供了统一的错误页面和异常信息。 **7. RESTful支持** Spring MVC支持构建RESTful服务,通过HTTP方法如GET、POST、PUT、DELETE等,实现...

    spring mvc 4.0

    在Spring MVC 4.0版本中,它引入了许多改进和新特性,以提升开发效率和应用程序的性能。 1. **依赖注入**:Spring MVC 4.0继续支持Spring框架的核心功能,依赖注入(DI),允许开发者通过配置来管理对象及其依赖...

    Spring mvc 教程

    - **@ExceptionHandler 注解**:用于声明异常处理方法。 - **处理一般的 Spring MVC 异常**:Spring MVC 自身的一些异常可以在这里统一处理。 - **使用 @ResponseStatus 注解业务异常**:为自定义异常添加 HTTP 状态...

    Spring mvc5.0.3 所有jar包

    下面我们将深入探讨Spring MVC 5.0.3中的关键知识点。 1. **DispatcherServlet**: 作为Spring MVC的核心组件,DispatcherServlet负责接收HTTP请求,然后根据配置的映射规则将请求分发到相应的处理器(Controller)...

    spring mvc框架依赖全面jar

    `org.springframework.web-3.1.1.RELEASE.jar` 包含了Spring框架对Web应用的支持,如过滤器、异常处理和请求映射等。 `javassist-3.12.0.GA.jar` 是Java编程辅助工具,它在运行时可以动态修改类和创建新的类,常...

    spring-mvc-官方中文文档

    Spring MVC 提供了丰富的功能,包括请求处理、视图解析、数据绑定、异常处理等,旨在简化 Web 开发流程。 1. **Spring MVC 概述**:Spring MVC 是基于 Spring 框架构建的,它遵循 MVC 设计模式,将业务逻辑、数据和...

    spring MVC异常处理(IDEA)

    spring MVC异常处理(IDEA)

    Spring MVC入门教程

    十一、spring mvc 如何实现全局的异常处理? 十二、spring mvc 如何把全局异常记录到日志中? 十三、如何给spring3 MVC中的Action做JUnit单元测试? 十四、spring mvc 转发与重定向 十五、spring mvc 处理ajax请求 ...

    精通Spring MVC4

    Spring MVC提供了一种优雅的异常处理机制,可以使用@ControllerAdvice和@ExceptionHandler注解定义全局异常处理器。此外,还可以自定义错误页面,提高用户体验。 8. **数据绑定和验证** Spring MVC支持自动的数据...

Global site tag (gtag.js) - Google Analytics