春季最高

通过参考资料开始使用Spring 5和Spring Boot 2学习的春天课程:

> >学习春天
休息前

通过参考资料开始使用Spring 5和Spring Boot 2学习的春天课程:

>>看看这个课程

1.概述

本教程将说明如何用Spring实现REST API的异常处理。我们还将获得一些历史概述,看看不同版本引入了哪些新选项。

在Spring 3.2之前,Spring MVC应用程序中处理异常的两种主要方法是HandlerExceptionResolver或者是@ExceptionHandler注释。两者都有一些明确的缺点。

自3.2以来,我们有了@ControllerAdvice注释解决前两个解决方案的局限性,并在整个应用程序中推广统一的异常处理。

现在Spring 5引入了ResponseStatusException- 在我们的REST API中进行基本错误处理的快速方法。

所有这些都有一个共同点:它们处理的是关注点分离很好。该应用程序可以抛出异常,以指示某种故障,然后将单独处理。

最后,我们将看到春天的靴子带给桌面以及我们如何配置它以满足我们的需求。

进一步阅读:

REST API的自定义错误消息处理

使用Spring为REST API实现一个全局异常处理程序。

Spring Data REST验证器指南

Spring Data REST验证器的快速实用指南

Spring MVC自定义验证

学习如何构建自定义验证注释并在Spring MVC中使用它。

2.解决方案1:控制器级@ExceptionHandler

第一个解决方案在工作@ controller等级。我们将定义一种处理异常和注释的方法@ExceptionHandler:

公共类foocontroller {// ... @exceptionhandler({cortomexception1.class,customexception2.class})public void handlexception(){//}}

这种方法有一个主要的缺点:T@ExceptionHandler带注释的方法只对那个特定的控制器是活动的,而不是整个应用程序的全局。当然,将其添加到每个控制器会使其不适用于一般的异常处理机制。

我们可以通过所有控制器都扩展了基础控制器类。

然而,这种解决方案可能会成为应用程序的一个问题,无论出于何种原因,这是不可能的。例如,控制器可能已经从另一个基类扩展,这些基类可能在另一个jar中或者不能直接修改,或者它们本身不能直接修改。

接下来,我们将研究解决异常处理问题的另一种方法——这种方法是全局的,不包括对现有构件(如Controllers)的任何更改。

3.解决方案2:HandlerExceptionResolver

第二种解决方案是定义一个HandlerExceptionResolver。这将解决应用程序抛出的任何异常。它还允许我们实现统一异常处理机制在REST API中。

在使用自定义解析器之前,我们先来看看现有的实现。

3.1。ExceptionHandlerExceptionResolver

该旋转变压器在Spring 3.1中引入,默认情况下启用DispatcherServlet。这实际上是如何@ExceptionHandler机械学提出了较早的作品。

3.2。defaulthandlerexcepceionResolver.

这个解析器是在Spring 3.0中引入的,在DispatcherServlet

它用于解决它们对应的标准春季异常HTTP状态代码,即客户端错误4 xx和服务器错误5xx.状态码。这是完整的列表以及它们如何映射到状态代码。

虽然它确实正确地设置了响应的状态代码限制是它没有对Response的主体设置任何内容。对于REST API - 状态代码实际上是不够的信息呈现给客户端 - 响应必须具有一个主体,允许应用程序提供有关故障的其他信息。金宝搏官网188be

这可以通过配置视图分辨率和渲染错误内容来解决Modelandview.,但解决方案显然不是最佳的。这就是为什么Spring 3.2引入了一个更好的选项,我们将在后面的小节中讨论这个选项。

3.3。ResponseStatusExceptionResolver

这个解析器也在Spring 3.0中引入,并且在DispatcherServlet

它的主要职责是使用@ResponseStatus注释可用于自定义异常,并将这些异常映射到HTTP状态代码。

这样一个自定义异常可能看起来像:

@ResponseStatus(value = httpstatus.not_found)public类myresourcenotfoundexception扩展了runtimeexception {public myresourcenotfoundException(){super();public myresourcenotfoundException(字符串消息,克拉巴原因){super(消息,原因);public myResourcenotFoundException(String Message){Super(消息);public myresourcenotfoundException(throwable原因){超级(原因);}}

defaulthandlerexcepceionResolver.,这个解析器在处理响应体的方式上受到限制——它确实将状态码映射到响应上,但响应体仍然是空值。

3.4。simpleMappingExceptionResolver.AnnotationMethodHandlerExceptionResolver

simpleMappingExceptionResolver.已经存在很长一段时间了。它起源于较旧的Spring MVC模型与REST服务无关。我们基本上是用它来将异常类名映射到视图名。

AnnotationMethodHandlerExceptionResolver在Spring 3.0中引入了@ExceptionHandler注释,但已被ExceptionHandlerExceptionResolver从Spring 3.2开始。

3.5。自定义HandlerExceptionResolver

的结合defaulthandlerexcepceionResolver.ResponseStatusExceptionResolver对于为春季宁静的服务提供良好的错误处理机制,还有很长的路要走。如前所述,缺点是没有对响应体的控制。

理想情况下,我们希望能够输出JSON或XML,具体取决于客户端要求的格式(通过接受标题)。

这个单独证明创造一个新的自定义异常解析器:

@Component public class RestResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver {@Override protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {try {if (exinstanceof IllegalArgumentException) {return handleIllegalArgument((IllegalArgumentException) ex, response, handler);}……} catch (Exception handlerException) {log . Exception;warn("Handling of [" + ex.getClass().getName() + "] result in Exception", handlerException);}返回null;} private ModelAndView handleIllegalArgument(IllegalArgumentException ex, HttpServletResponse response)抛出IOException {response. senderror (HttpServletResponse. sc_conflict);String accept = request.getHeader(HttpHeaders.ACCEPT);…返回新ModelAndView (); } }

这里需要注意的一个细节是,我们可以访问要求本身,所以我们可以考虑的价值接受客户端发送的头。

例如,如果客户要求application / json,然后,在错误条件的情况下,我们希望确保返回用application / json

另一个重要的实现细节是我们返回一个Modelandview.-这是响应的主体,我们可以在上面放任何需要的东西。

这种方法是一种用于Spring REST服务错误处理的一致且易于配置的机制。

然而,它确实有局限性:它与底层交互HtttpServletResponse适合旧的MVC模型使用Modelandview.,所以还有改进的空间。

4.解决方案3:@ControllerAdvice

Spring 3.2带来支持一个全球@ExceptionHandler与之@ControllerAdvice注释。

这使得一种机制能够脱离旧的MVC模型并利用ResponseEntity随着类型的安全性和灵活性@ExceptionHandler:

@ControllerAdvice public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {@ExceptionHandler(value = {IllegalArgumentException.class, IllegalStateException.class}) protected ResponseEntity handleConflict(RuntimeException ex,WebRequest请求){String bodyOfResponse = "这应该是应用程序特定的";return handleExceptionInternal(ex, bodyOfResponse, new HttpHeaders(), HttpStatus。冲突,请求);}}
         

@ControllerAdvice注释允许我们巩固我们的多元,分散@ExceptionHandlerS转换为单个全局错误处理组件。

实际机制非常简单,但也非常灵活:

  • 它使我们能够完全控制响应体和状态代码。
  • 它将多个异常映射到同一个方法,以便一起处理。
  • 它很好地利用了较新的RESTfulResposeEntity响应。

这里要记住的一件事是与声明的异常匹配@ExceptionHandler作为方法参数的异常。

如果它们不匹配,编译器就不会抱怨——它没有理由抱怨——Spring也不会抱怨。

但是,当实际在运行时抛出异常时,异常解析机制将失败:

java.lang.IllegalStateException: No suitable resolver for argument [0] [type=…]HandlerMethod details:…

5.解决方案4:ResponseStatusException(春季5及以上)

Spring 5引入了ResponseStatusException类。

我们可以创建它的实例,提供HttpStatus和一个可选的原因A.原因:

@GetMapping(value = "/{id}") public Foo findById(@PathVariable("id") Long id, HttpServletResponse response) {try {Foo resourceById = restpreconditionescheckfound (service.findOne(id));eventPublisher。publishEvent(新SingleResourceRetrievedEvent(这、响应);返回resourceById;} throw new ResponseStatusException(HttpStatus. conf) {throw new ResponseStatusException(HttpStatus. conf . conf);NOT_FOUND, "Foo Not Found", exc);}}

使用的好处是什么ResponseStatusException吗?

  • 非常适合创建原型:我们可以非常快速地执行基本解决方案。
  • 一种类型,多个状态代码:一个异常类型可以导致多个不同的响应。这减少了紧耦合相比@ExceptionHandler
  • 我们不需要创建那么多自定义异常类。
  • 我们有对异常处理的更多控制因为异常可以通过编程方式创建。

那么权衡呢?金宝搏官网188be

  • 异常处理没有统一的方法:与之相反,在应用程序范围内实施一些约定更加困难@ControllerAdvice,它提供了一种全局方法。
  • 代码复制:我们可能会发现自己在多个控制器中复制代码。

我们还应该注意,在一个应用程序中可以组合不同的方法。

例如,我们可以实现@ControllerAdvice在全球范围内也ResponseStatusException在本地。

但是,我们需要小心:如果同一个异常可以用多种方式处理,我们可能会注意到一些令人惊讶的行为。一种可能的约定是总是以一种方式处理一种特定的异常。

有关更多细节和更多示例,请参见我们的教程ResponseStatusException

6.处理Spring安全中拒绝的访问

当经过身份验证的用户试图访问他没有足够权限访问的资源时,就会发生Access Denied。

6.1。自定义错误页面

首先,让我们看看解决方案的MVC风格,并了解如何为Access Denied定制错误页面。

XML配置:

 …< br /> < br />  < br /> < br />  < br /> < br /

以及Java配置:

@Override void configure(HttpSecurity http) throws Exception {http. authorizerequests () .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN") ... .and() .exceptionHandling().accessDeniedPage("/my-error-page");}

当用户尝试访问资源而不有足够的权限时,它们将被重定向到“/ my-error-page”

6.2。自定义AccessDeniedHandler

接下来,让我们看看如何编写我们的自定义AccessDeniedHandler:

@Component public class CustomAccessDeniedHandler实现AccessDeniedHandler {@Override public void handle (HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) throws IOException, ServletException {response. sendredirect ("/my-error-page");}}

现在让我们使用XML配置:

 …< br /> < br />  < br /> < br />  < br />  < br />  < br /

0r使用Java配置:

@Autowired私有CustomAccessDeniedHandler accessDeniedHandler;@Override protected void configure(HttpSecurity http) throws Exception {http. authorizerequests () .antMatchers("/admin/*").hasAnyRole("ROLE_ADMIN") ... .and() .exceptionHandling().accessDeniedHandler(accessDeniedHandler)}

注意在我们的CustomAccessDeniedHandler,我们可以通过重定向或显示自定义错误消息来定制响应。

6.3。REST和方法级安全性

最后,让我们看看如何处理方法级安全性@PreAuthorize,@PostAuthorize,@ secure拒绝访问。

当然,我们将使用前面讨论的全局异常处理机制来处理AccessDeniedException:

@ControllerAdvice公开课RestResponseEntityExceptionHandler延伸ResponseEntityExceptionHandler {@ExceptionHandler ({AccessDeniedException.class})公共ResponseEntity <对象> handleAccessDeniedException(异常前,WebRequest请求){返回新ResponseEntity <对象>(“拒绝访问”消息在这里,新HttpHeaders (),HttpStatus.FORBIDDEN);}……}

7.Spring引导支持

Spring Boot提供一个ErrorController实现以合理的方式处理错误。

简而言之,它为浏览器提供了一个回退错误页面(也称为Whitelabel错误页面),并为RESTful、非html请求提供了一个JSON响应:

{"timestamp": "2019-01-17T16:12:45.977+0000", "status": 500, "error": "Internal Server error", "message": "error processing the request!", "path": "/my-endpoint-with-exceptions"}

像往常一样,Spring Boot允许用属性配置这些特性:

  • server.Error.WhitElabel.Enabled.:可以用来禁用Whitelabel错误页面,并依赖servlet容器提供一个HTML错误消息
  • server.error.include-stacktrace:一个总是价值;在HTML和JSON默认响应中都包含堆栈跟踪
  • server.error.include-message:从2.3版开始,Spring Boot隐藏了消息在响应字段中避免敏感信息泄露;我们可以用这个性质总是价值来启用它

除了这些属性,我们可以为/提供自己的视图解析器映射错误,覆盖白标签页。

还可以自定义希望在响应中显示的属性,方法是包含ErrorAttributes上下文中的Bean。我们可以扩展DefaultErrorAttributesSpring Boot提供的类使事情变得更简单:

@Component公共类MyCustomErrorAttributes扩展了DefaulTerrorAttributes {@Override公共映射 GetErrorAttributes(WebRequest WebRequest,ErrerAttributeOptions选项){Map  ErrorAttributes = super.geterrorAttributes(WebRequest,选项);ErrerAttributes.put(“locale”,webrequest.getLocale().tostring());ErrerAttributes.remove(“错误”);// ...返回ErrorAttributes;}}

如果我们想进一步定义(或覆盖)应用程序如何处理特定内容类型的错误,我们可以注册ErrorController豆角,扁豆。

同样,我们可以利用默认值BasicErrorController由Spring Boot提供帮助。

例如,假设我们想定制应用程序如何处理XML端点中触发的错误。类定义一个公共方法@requestmappping.,并说明它产生应用程序/ xml媒体类型:

@Component public class MyErrorController extends BasicErrorController {public MyErrorController(ErrorAttributes, ServerProperties, ServerProperties) {super(ErrorAttributes, ServerProperties . geterror ());@RequestMapping(produces = MediaType.APPLICATION_XML_VALUE) public ResponseEntity> xmlError(HttpServletRequest request){//…}}

注意:这里我们仍然依赖于server.error。*在项目中定义的引导属性被绑定到ServerProperties豆角,扁豆。

8.结论

本文讨论了在春季中实现了速率API的异常处理机制的几种方法,从较旧的机制开始,并继续弹簧3.2支持,进入4.x和5.x。

与往常一样,本文中提供的代码是可用的在GitHub

对于Spring security相关的代码,您可以检查spring-security-rest模块。

春天底

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

> >这门课程
休息下

通过。开始使用Spring 5和Spring Boot 2学习的春天课程:

>>看看这个课程
21注释
最古老的
最新的
内联反馈
查看所有评论
本文评论关闭!