From 65fdd0efebd95f36904256cc0b5d9038287622ea Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Sun, 1 Apr 2018 22:10:41 -0400 Subject: [PATCH] [docs] Updates for exception handling in web sections Issue: SPR-16394 --- src/docs/asciidoc/web/webflux.adoc | 248 ++++++++++++++++++++++------- src/docs/asciidoc/web/webmvc.adoc | 232 +++++++++++++++++++++------ 2 files changed, 372 insertions(+), 108 deletions(-) diff --git a/src/docs/asciidoc/web/webflux.adoc b/src/docs/asciidoc/web/webflux.adoc index c1da61f5a2..52b2b741ac 100644 --- a/src/docs/asciidoc/web/webflux.adoc +++ b/src/docs/asciidoc/web/webflux.adoc @@ -448,31 +448,21 @@ This can be automated through the use of [[webflux-web-handler-api]] -=== WebHandler +=== WebHandler API -`HttpHandler` is the lowest level contract for running on different HTTP servers. -On top of that foundation, the WebHandler API provides a slightly higher level, but -still general purpose, set of components that form a chain of -{api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler's], -{api-spring-framework}/web/server/WebFilter.html[WebFilter's], and a -{api-spring-framework}/web/server/WebHandler.html[WebHandler]. +The WebHandler API is a general purpose, server, web API for processing requests through a +chain of {api-spring-framework}/web/server/WebExceptionHandler.html[WebExceptionHandler's], +{api-spring-framework}/web/server/WebFilter.html[WebFilter's], and a target +{api-spring-framework}/web/server/WebHandler.html[WebHandler]. The chain can be assembled +with `WebHttpHandlerBuilder` either by adding components to the builder or by having them +detected from a Spring `ApplicationContext`. The builder returns an +<> that can then be used to run on any of the supported servers. -All WebHandler API components take `ServerWebExchange` as input which goes beyond -`ServerHttpRequest` and `ServerHttpResponse` to provide extra building blocks for -use in web applications such as request attributes, session attributes, access to parsed -form data, multipart data, and more. - -`WebHttpHandlerBuilder` is used to assemble a request processing chain. You can use -methods on the builder to add components manually, or more likely have them detected from -a Spring `ApplicationContext`, with the resulting `HttpHandler` ready to run via a -<>: - -[source,java,indent=0] -[subs="verbatim,quotes"] ----- -ApplicationContext context = ... -HttpHandler handler = WebHttpHandlerBuilder.applicationContext(context).build() ----- +While `HttpHandler` aims to be the most minimal contract across HTTP servers, the +WebHandler API provides essential features commonly used to build web applications. +For example, the `ServerWebExchange` available to WebHandler API components provides +access not only to the request and response, but also to request and session attributes, +access to parsed form data, multipart data, and more. @@ -488,12 +478,14 @@ The table below lists the components that `WebHttpHandlerBuilder` detects: | | `WebExceptionHandler` | 0..N -| Exception handlers to apply after all ``WebFilter``'s and the target `WebHandler`. +| Provide handling for exceptions from the chain of ``WebFilter``'s and the target + `WebHandler`. For more details, see <>. | | `WebFilter` | 0..N -| Filters to invoke before and after the target `WebHandler`. +| Apply interception style logic to before and after the rest of the filter chain and + the target `WebHandler`. For more details, see <>. | "webHandler" | `WebHandler` @@ -645,9 +637,13 @@ a heartbeat and ignore. === Filters [.small]#<># -As part of the <>, the `spring-web` module provides a number of -`WebFilter` implementations. +In the <>, a `WebFilter` can be used to apply interception-style +logic before and after the rest of the processing chain of filters and the target +`WebHandler`. When using the <>, registering a `WebFilter` is as simple +as declaring it as a Spring bean, and optionally expressing precedence via `@Order` on +the bean declaration or by implementing `Ordered`. +The following describe the available `WebFilter` implementations: [[webflux-filters-forwarded-headers]] @@ -690,6 +686,37 @@ See the section on <> and the <> for more +[[webflux-exception-handler]] +=== Exceptions +[.small]#<># + +In the <>, a `WebExceptionHandler` can be used to to handle +exceptions from the chain of ``WebFilter``'s and the target `WebHandler`. When using the +<>, registering a `WebExceptionHandler` is as simple as declaring it as a +Spring bean, and optionally expressing precedence via `@Order` on the bean declaration or +by implementing `Ordered`. + +Below are the available `WebExceptionHandler` implementations: + +[cols="1,2", options="header"] +|=== +| Exception Handler | Description + +| `ResponseStatusExceptionHandler` +| Provides handling for exceptions of type + {api-spring-framework}/web/server/ResponseStatusException.html[ResponseStatusException] + by setting the response to the HTTP status code of the exception. + +| `WebFluxResponseStatusExceptionHandler` +| Extension of `ResponseStatusExceptionHandler` that can also determine the HTTP status + code an `@ResponseStatus` annotation on any exception. + + This handler is declared in the <>. + +|=== + + + [[webflux-dispatcher-handler]] == DispatcherHandler @@ -805,24 +832,60 @@ processing by writing to the response directly or using a view to render. [[webflux-resulthandling]] === Result Handling -When `DispatcherHandler` needs to process the return value from a handler, it finds a -`HandlerResultHandler` that support it and invokes it. The available implementations are -listed below with their default order (all are declared in the <>): +The return value from the invocation of a handler, through a `HandlerAdapter`, is wrapped +as `HandlerResult`, along with some additional context, and passed to the first +`HandlerResultHandler` that claims support for it. The table below shows the available +`HandlerResultHandler` implementations all of which are declared in the <>: + +[cols="1,2,1", options="header"] +|=== +| Result Handler Type | Return Values | Default Order + +| `ResponseEntityResultHandler` +| `ResponseEntity`, typically from ``@Controller``'s. +| 0 + +| `ServerResponseResultHandler` +| `ServerResponse`, typically from functional endpoints. +| 0 + +| `ResponseBodyResultHandler` +| Handle return values from `@ResponseBody` methods or `@RestController` classes. +| 100 + +| `ViewResolutionResultHandler` +| `CharSequence` or {api-spring-framework}/web/reactive/result/view/View.html[View], + {api-spring-framework}/ui/Model.html[Model] or `Map`, + {api-spring-framework}/web/reactive/result/view/Rendering.html[Rendering], + or any other Object is treated as a model attribute. + + Also see <>. +| `Integer.MAX_VALUE` + +|=== + + + +[[webflux-dispatcher-exceptions]] +=== Exceptions +[.small]#<># + +The `HandlerResult` returned from a `HandlerAdapter` may expose a function for error +handling based on some handler-specific mechanism. This error function is called if: + +* the handler (e.g. `@Controller`) invocation fails. +* handling of the handler return value through a `HandlerResultHandler` fails. -* `ResponseEntityResultHandler` -- handles `ResponseEntity` return values typically -returned from annotated controllers. The order is set to 0 since it safely matches return -values by type. -* `ServerResponseResultHandler` -- supports `ServerResponse` return values typically -returned from functional endpoints. The order is set to 0 since it safely matches return -values by type. -* `ResponseBodyResultHandler` -- handles return values from `@ResponseBody` methods or -`@RestController` classes. The order is set to 100, i.e. after result handlers that -check for a specific type. -* `ViewResolutionResultHandler` -- performs the <> algorithm for -HTML template rendering. The order is set to `Ordered.LOWEST_PRECEDENCE` since it -supports several specific types, e.g. `String`, `Map`, `Rendering`, and others, but will -also treat any other Object as a model attribute. This is why it needs to be last in -the order. +The error function can change the response, e.g. to an error status, as long as an error +signal occurs before the reactive type returned from the handler produces any data items. + +This is how `@ExceptionHandler` methods in `@Controller` classes are supported. +By contrast, support for the same in Spring MVC is built on a `HandlerExceptionResolver`. +This generally shouldn't matter, however, keep in mind that in WebFlux you cannot use a +`@ControllerAdvice` to handle exceptions that occur before a handler is chosen. + +See also <> in the Annotated Controller section, or +<> in the WebHandler API section. @@ -2159,15 +2222,19 @@ controller method. Use a composite interface if you need to activate multiple vi [[webflux-ann-modelattrib-methods]] -=== Model Methods +=== Model [.small]#<># -The `@ModelAttribute` annotation can be used on `@RequestMapping` -<> to create or access an Object -from the model and bind it to the request. `@ModelAttribute` can also be used as a -method-level annotation on controller methods whose purpose is not to handle requests -but to add commonly needed model attributes prior to request handling. +The `@ModelAttribute` annotation can be used: + +* On a <> in `@RequestMapping` methods +to create or access an Object from the model, and to bind it to the request through a +`WebDataBinder`. +* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes helping +to initialize the model prior to any `@RequestMapping` method invocation. +* On a `@RequestMapping` method to mark its return value is a model attribute. +This section discusses `@ModelAttribute` methods, or the 2nd from the list above. A controller can have any number of `@ModelAttribute` methods. All such methods are invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute` method can also be shared across controllers via `@ControllerAdvice`. See the section on @@ -2253,14 +2320,16 @@ as a view name. `@ModelAttribute` can also help to customize the model attribute [[webflux-ann-initbinder]] -=== Binder Methods +=== DataBinder [.small]#<># -`@InitBinder` methods in an `@Controller` or `@ControllerAdvice` class can be used to -customize type conversion for method arguments that represent String-based request values -(e.g. request parameters, path variables, headers, cookies, and others). Type conversion -also applies during data binding of request parameters onto `@ModelAttribute` arguments -(i.e. command objects). +`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods in order to +initialize instances of `WebDataBinder`, and those in turn are used to: + +* Bind request parameters (i.e. form data or query) to a model object. +* Convert String-based request values such as request parameters, path variables, +headers, cookies, and others, to the target type of controller method arguments. +* Format model object values as String values when rendering HTML forms. `@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or Spring `Converter` and `Formatter` components. In addition, the @@ -2310,6 +2379,73 @@ controller-specific ``Formatter``'s: +[[webflux-ann-controller-exceptions]] +=== Exceptions +[.small]#<># + +`@Controller` and <> classes can have +`@ExceptionHandler` methods to handle exceptions from controller methods. For example: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + @Controller + public class SimpleController { + + // ... + + @ExceptionHandler + public ResponseEntity handle(IOException ex) { + // ... + } + + } +---- + +The annotation can list the exception types to match. Or simply declare the target +exception as a method argument as shown above. When multiple exception methods match, +a root exception match is generally preferred to a cause exception match. More formally +the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the +thrown exception type. + +In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception +mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root +exception match is preferred to a cause, this is mainly among the methods of a given +controller or `@ControllerAdvice`. That means a cause match on a higher-priority +`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority +`@ControllerAdvice`. + +Support for `@ExceptionHandler` methods in Spring WebFlux is provided by the +`HandlerAdapter` for `@RequestMapping` methods. See <> +under the `DispatcherHandler` section for more details. + + +An `@ExceptionHandler` method in WebFlux supports the same method arguments and return +values as an `@RequestMapping` method does with the exception of request body and +`@ModelAttribute` related method arguments. + + +[[webflux-ann-rest-exceptions]] +==== REST API exceptions +[.small]#<># + +A common requirement for REST services is to include error details in the body of the +response. The Spring Framework does not automatically do this because the representation +of error details in the response body is application specific. However a +`@RestController` may use `@ExceptionHandler` methods with a `ResponseEntity` return +value to set the status and the body of the response. Such methods may also be declared +in `@ControllerAdvice` classes to apply them globally. + +[NOTE] +==== +Note that Spring WebFlux does not have an equivalent for the Spring MVC +`ResponseEntityExceptionHandler` because WebFlux only raises `ResponseStatusException` +(or sub-classes of), which and those do not need to be translated translation to an HTTP +status code. +==== + + + [[webflux-ann-controller-advice]] === Controller Advice [.small]#<># diff --git a/src/docs/asciidoc/web/webmvc.adoc b/src/docs/asciidoc/web/webmvc.adoc index b2089e5b27..9cae3e7bd6 100644 --- a/src/docs/asciidoc/web/webmvc.adoc +++ b/src/docs/asciidoc/web/webmvc.adoc @@ -507,13 +507,13 @@ declare it as an <> bean or configure it directly on [[mvc-exceptionhandlers]] -=== Exception Resolution +=== Exceptions +[.small]#<># -If an exception occurs during the mapping or the invocation of a request handler (e.g. an -`@Controller`), the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` -beans to try and resolve the exception and to provide alternative handling for it, which -typically means preparing an error response whether an HTML error page, an error status, -or both. +If an exception occurs during request mapping or is thrown from a request handler such as +an `@Controller`, the `DispatcherServlet` delegates to a chain of `HandlerExceptionResolver` +beans to resolve the exception and provide alternative handling, which typically is an +error response. The table below lists the available `HandlerExceptionResolver` implementations: @@ -536,29 +536,27 @@ The table below lists the available `HandlerExceptionResolver` implementations: | `ExceptionHandlerExceptionResolver` | Resolves exceptions by invoking an `@ExceptionHandler` method in an `@Controller` or an - `@ControllerAdvice` class. See <>. + `@ControllerAdvice` class. See <>. |=== [[mvc-excetionhandlers-handling]] -==== Handling +==== Chain of resolvers -You chain exception resolvers by declaring more than one exception resolver beans and, -if necessary, setting the `order` property to specify ordering. Remember, the higher the -order property, the later the exception resolver is positioned in the chain. +You can form an exception resolver chain simply by declaring multiple `HandlerExceptionResolver` +beans in your Spring configuration and setting their `order` properties as needed. +The higher the order property, the later the exception resolver is positioned. -The contract of `HandlerExceptionResolver` specifies that it __can__ return: +The contract of `HandlerExceptionResolver` specifies that it can return: * `ModelAndView` that points to an error view. * Empty `ModelAndView` if the exception was handled within the resolver. -* `null` if the exception remains unresolved, for subsequent resolvers to try; if the -exception remains unresolved by any resolver, it is re-thrown and left to propagate to -the Servlet container. +* `null` if the exception remains unresolved, for subsequent resolvers to try; and if the +exception remains at the end, it is allowed to bubble up to the Servlet container. -To configure exception handling is as simple as adding `HandlerExceptionResolver` beans -to your Spring configuration. The <> automatically declares built-in -resolvers for default Spring MVC exceptions, for `@ResponseStatus` annotated exceptions, -and for support of `@ExceptionHandler` methods. You can customize that list or replace it. +The <> automatically declares built-in resolvers for default Spring MVC +exceptions, for `@ResponseStatus` annotated exceptions, and for support of +`@ExceptionHandler` methods. You can customize that list or replace it. [[mvc-ann-customer-servlet-container-error-page]] @@ -2655,15 +2653,19 @@ customized through `jsonpParameterNames` property. [[mvc-ann-modelattrib-methods]] -=== Model Methods +=== Model [.small]#<># -The `@ModelAttribute` annotation can be used on `@RequestMapping` -<> to create or access an Object -from the model and bind it to the request. `@ModelAttribute` can also be used as a -method-level annotation on controller methods whose purpose is not to handle requests -but to add commonly needed model attributes prior to request handling. +The `@ModelAttribute` annotation can be used: + +* On a <> in `@RequestMapping` methods +to create or access an Object from the model, and to bind it to the request through a +`WebDataBinder`. +* As a method-level annotation in `@Controller` or `@ControllerAdvice` classes helping +to initialize the model prior to any `@RequestMapping` method invocation. +* On a `@RequestMapping` method to mark its return value is a model attribute. +This section discusses `@ModelAttribute` methods, or the 2nd from the list above. A controller can have any number of `@ModelAttribute` methods. All such methods are invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute` method can also be shared across controllers via `@ControllerAdvice`. See the section on @@ -2728,14 +2730,16 @@ the model attribute name: [[mvc-ann-initbinder]] -=== Binder Methods +=== DataBinder [.small]#<># -`@InitBinder` methods in an `@Controller` or `@ControllerAdvice` class can be used to -customize type conversion for method arguments that represent String-based request values -(e.g. request parameters, path variables, headers, cookies, and others). Type conversion -also applies during data binding of request parameters onto `@ModelAttribute` arguments -(i.e. command objects). +`@Controller` or `@ControllerAdvice` classes can have `@InitBinder` methods in order to +initialize instances of `WebDataBinder`, and those in turn are used to: + +* Bind request parameters (i.e. form data or query) to a model object. +* Convert String-based request values such as request parameters, path variables, +headers, cookies, and others, to the target type of controller method arguments. +* Format model object values as String values when rendering HTML forms. `@InitBinder` methods can register controller-specific `java.bean.PropertyEditor`, or Spring `Converter` and `Formatter` components. In addition, the @@ -2787,15 +2791,11 @@ controller-specific ``Formatter``'s: [[mvc-ann-exceptionhandler]] -=== Exception Methods - -`@ExceptionHandler` methods in an `@Controller` can be used to handle exceptions during -request handling from the same controller. An `@ExceptionHandler` can also be declared -in an <> to apply across controllers. -Support for `@ExceptionHandler` methods in Spring MVC is provided through the -<> mechanism. +=== Exceptions +[.small]#<># -Below is an example: +`@Controller` and <> classes can have +`@ExceptionHandler` methods to handle exceptions from controller methods. For example: [source,java,indent=0] [subs="verbatim,quotes"] @@ -2805,7 +2805,7 @@ Below is an example: // ... - @ExceptionHandler(IOException.class) + @ExceptionHandler public ResponseEntity handle(IOException ex) { // ... } @@ -2813,22 +2813,150 @@ Below is an example: } ---- -The value of the `@ExceptionHandler` annotation can be set to an array of Exception types -to match to. Or if the annotation value is not set, then the exception type declared in -the method signature is used instead. `@ExceptionHandler` methods can declare other -arguments too, e.g. the `HttpServletRequest`. The return value type can be a `String`, -which is interpreted as a view name, a `ModelAndView` object, a `ResponseEntity`, or you -can also add the `@ResponseBody` annotation. +The annotation can list the exception types to match. Or simply declare the target +exception as a method argument as shown above. When multiple exception methods match, +a root exception match is generally preferred to a cause exception match. More formally +the `ExceptionDepthComparator` is used to sort exceptions based on their depth from the +thrown exception type. + +In a multi-`@ControllerAdvice` arrangement, please declare your primary root exception +mappings on a `@ControllerAdvice` prioritized with a corresponding order. While a root +exception match is preferred to a cause, this is mainly among the methods of a given +controller or `@ControllerAdvice`. That means a cause match on a higher-priority +`@ControllerAdvice` is preferred to any match (e.g. root) on a lower-priority +`@ControllerAdvice`. + +Support for `@ExceptionHandler` methods in Spring MVC is built on the `DispatcherServlet` +level, <> mechanism. + + +[[mvc-ann-exceptionhandler-args]] +==== Method arguments + +`@ExceptionHandler` methods support the following arguments: + +[cols="1,2", options="header"] +|=== +| Method argument | Description + +| Exception type +| For access to the raised exception. + +| `HandlerMethod` +| For access to the controller method that raised the exception. + +| `WebRequest`, `NativeWebRequest` +| Generic access to request parameters, request & session attributes, without direct + use of the Servlet API. + +| `javax.servlet.ServletRequest`, `javax.servlet.ServletResponse` +| Choose any specific request or response type -- e.g. `ServletRequest`, `HttpServletRequest`, + or Spring's `MultipartRequest`, `MultipartHttpServletRequest`. + +| `javax.servlet.http.HttpSession` +| Enforces the presence of a session. As a consequence, such an argument is never `null`. + + **Note:** Session access is not thread-safe. Consider setting the + ``RequestMappingHandlerAdapter``'s "synchronizeOnSession" flag to "true" if multiple + requests are allowed to access a session concurrently. + +| `java.security.Principal` +| Currently authenticated user; possibly a specific `Principal` implementation class if known. + +| `HttpMethod` +| The HTTP method of the request. + +| `java.util.Locale` +| The current request locale, determined by the most specific `LocaleResolver` available, in + effect, the configured `LocaleResolver`/`LocaleContextResolver`. + +| `java.util.TimeZone` + `java.time.ZoneId` +| The time zone associated with the current request, as determined by a `LocaleContextResolver`. + +| `java.io.OutputStream`, `java.io.Writer` +| For access to the raw response body as exposed by the Servlet API. + +| `java.util.Map`, `org.springframework.ui.Model`, `org.springframework.ui.ModelMap` +| For access to the model for an error response, always empty. + +| `RedirectAttributes` +| Specify attributes to use in case of a redirect -- i.e. to be appended to the query + string, and/or flash attributes to be stored temporarily until the request after redirect. + See <> and <>. + +| `@SessionAttribute` +| For access to any session attribute; in contrast to model attributes stored in the session + as a result of a class-level `@SessionAttributes` declaration. See + <> for more details. + +| `@RequestAttribute` +| For access to request attributes. See <> for more details. + +|=== + + +[[mvc-ann-exceptionhandler-return-values]] +==== Return Values + +`@ExceptionHandler` methods support the following return values: + +[cols="1,2", options="header"] +|=== +| Return value | Description + +| `@ResponseBody` +| The return value is converted through ``HttpMessageConverter``s and written to the + response. See <>. + +| `HttpEntity`, `ResponseEntity` +| The return value specifies the full response including HTTP headers and body be converted + through ``HttpMessageConverter``s and written to the response. + See <>. + +| `String` +| A view name to be resolved with ``ViewResolver``'s and used together with the implicit + model -- determined through command objects and `@ModelAttribute` methods. The handler + method may also programmatically enrich the model by declaring a `Model` argument + (see above). + +| `View` +| A `View` instance to use for rendering together with the implicit model -- determined + through command objects and `@ModelAttribute` methods. The handler method may also + programmatically enrich the model by declaring a `Model` argument (see above). + +| `java.util.Map`, `org.springframework.ui.Model` +| Attributes to be added to the implicit model with the view name implicitly determined + through a `RequestToViewNameTranslator`. + +| `@ModelAttribute` +| An attribute to be added to the model with the view name implicitly determined through + a `RequestToViewNameTranslator`. + + Note that `@ModelAttribute` is optional. See "Any other return value" further below in + this table. + +| `ModelAndView` object +| The view and model attributes to use, and optionally a response status. + +| `void` +| A method with a `void` return type (or `null` return value) is considered to have fully + handled the response if it also has a `ServletResponse`, or an `OutputStream` argument, or + an `@ResponseStatus` annotation. The same is true also if the controller has made a positive + ETag or lastModified timestamp check (see <> for details). + + If none of the above is true, a `void` return type may also indicate "no response body" for + REST controllers, or default view name selection for HTML controllers. + +| Any other return value +| If a return value is not matched to any of the above, by default it is treated as a + model attribute to be added to the model, unless it is a simple type, as determined by + {api-spring-framework}/beans/BeanUtils.html#isSimpleProperty-java.lang.Class-[BeanUtils#isSimpleProperty] + in which case it remains unresolved. +|=== -For `@ExceptionHandler` methods, a root exception match will be preferred to just -matching a cause of the current exception, among the handler methods of a particular -controller or advice bean. However, a cause match on a higher-priority `@ControllerAdvice` -will still be preferred to a any match (whether root or cause level) on a lower-priority -advice bean. As a consequence, when using a multi-advice arrangement, please declare your -primary root exception mappings on a prioritized advice bean with a corresponding order! [[mvc-ann-rest-exceptions]] ==== REST API exceptions +[.small]#<># A common requirement for REST services is to include error details in the body of the response. The Spring Framework does not automatically do this because the representation