Explicit coverage of root vs cause exception matching in MVC ref docs
Issue: SPR-16743
(cherry picked from commit a200df6)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,7 +25,6 @@ import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.config.BeanReference;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
@@ -53,13 +52,10 @@ import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConvert
|
||||
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
|
||||
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.bind.support.WebArgumentResolver;
|
||||
import org.springframework.web.method.support.CompositeUriComponentsContributor;
|
||||
@@ -110,10 +106,10 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
|
||||
*
|
||||
* <p>This class registers the following {@link HandlerExceptionResolver}s:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
||||
* through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
||||
* with @{@link ResponseStatus}.
|
||||
* with {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||
* exception types
|
||||
* </ul>
|
||||
@@ -155,6 +151,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";
|
||||
|
||||
|
||||
private static final boolean javaxValidationPresent =
|
||||
ClassUtils.isPresent("javax.validation.Validator",
|
||||
AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
|
||||
@@ -210,8 +207,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
|
||||
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
|
||||
|
||||
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
|
||||
RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
|
||||
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
|
||||
|
||||
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
|
||||
RuntimeBeanReference validator = getValidator(element, source, parserContext);
|
||||
@@ -285,44 +282,40 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
|
||||
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedCsInterceptorDef);
|
||||
|
||||
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
|
||||
exceptionHandlerExceptionResolver.setSource(source);
|
||||
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
|
||||
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
|
||||
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
|
||||
addResponseBodyAdvice(exceptionHandlerExceptionResolver);
|
||||
|
||||
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
|
||||
methodExceptionResolver.setSource(source);
|
||||
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
|
||||
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
|
||||
methodExceptionResolver.getPropertyValues().add("order", 0);
|
||||
addResponseBodyAdvice(methodExceptionResolver);
|
||||
if (argumentResolvers != null) {
|
||||
exceptionHandlerExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
|
||||
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
|
||||
}
|
||||
if (returnValueHandlers != null) {
|
||||
exceptionHandlerExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
|
||||
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
|
||||
}
|
||||
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);
|
||||
|
||||
String methodExceptionResolverName = readerContext.registerWithGeneratedName(exceptionHandlerExceptionResolver);
|
||||
|
||||
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
|
||||
responseStatusExceptionResolver.setSource(source);
|
||||
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
|
||||
String responseStatusExceptionResolverName =
|
||||
readerContext.registerWithGeneratedName(responseStatusExceptionResolver);
|
||||
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
|
||||
statusExceptionResolver.setSource(source);
|
||||
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
statusExceptionResolver.getPropertyValues().add("order", 1);
|
||||
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
|
||||
|
||||
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
|
||||
defaultExceptionResolver.setSource(source);
|
||||
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
defaultExceptionResolver.getPropertyValues().add("order", 2);
|
||||
String defaultExceptionResolverName =
|
||||
readerContext.registerWithGeneratedName(defaultExceptionResolver);
|
||||
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
|
||||
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(uriContributorDef, uriContributorName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
|
||||
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));
|
||||
|
||||
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
|
||||
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
|
||||
@@ -380,8 +373,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getContentNegotiationManager(Element element, Object source,
|
||||
ParserContext parserContext) {
|
||||
private RuntimeBeanReference getContentNegotiationManager(
|
||||
Element element, Object source, ParserContext parserContext) {
|
||||
|
||||
RuntimeBeanReference beanRef;
|
||||
if (element.hasAttribute("content-negotiation-manager")) {
|
||||
@@ -402,8 +395,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return beanRef;
|
||||
}
|
||||
|
||||
private void configurePathMatchingProperties(RootBeanDefinition handlerMappingDef, Element element,
|
||||
ParserContext parserContext) {
|
||||
private void configurePathMatchingProperties(
|
||||
RootBeanDefinition handlerMappingDef, Element element, ParserContext parserContext) {
|
||||
|
||||
Element pathMatchingElement = DomUtils.getChildElementByTagName(element, "path-matching");
|
||||
if (pathMatchingElement != null) {
|
||||
@@ -462,15 +455,13 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private String getAsyncTimeout(Element element) {
|
||||
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
|
||||
return (asyncElement != null) ? asyncElement.getAttribute("default-timeout") : null;
|
||||
return (asyncElement != null ? asyncElement.getAttribute("default-timeout") : null);
|
||||
}
|
||||
|
||||
private RuntimeBeanReference getAsyncExecutor(Element element) {
|
||||
Element asyncElement = DomUtils.getChildElementByTagName(element, "async-support");
|
||||
if (asyncElement != null) {
|
||||
if (asyncElement.hasAttribute("task-executor")) {
|
||||
return new RuntimeBeanReference(asyncElement.getAttribute("task-executor"));
|
||||
}
|
||||
if (asyncElement != null && asyncElement.hasAttribute("task-executor")) {
|
||||
return new RuntimeBeanReference(asyncElement.getAttribute("task-executor"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -571,7 +562,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
if (jackson2XmlPresent) {
|
||||
RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source);
|
||||
Class<?> type = MappingJackson2XmlHttpMessageConverter.class;
|
||||
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
|
||||
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
|
||||
jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
|
||||
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
|
||||
@@ -582,7 +574,8 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
if (jackson2Present) {
|
||||
RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source);
|
||||
Class<?> type = MappingJackson2HttpMessageConverter.class;
|
||||
RootBeanDefinition jacksonConverterDef = createConverterDefinition(type, source);
|
||||
GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
|
||||
jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
|
||||
messageConverters.add(jacksonConverterDef);
|
||||
@@ -609,7 +602,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return beanDefinition;
|
||||
}
|
||||
|
||||
|
||||
private ManagedList<Object> extractBeanSubElements(Element parentElement, ParserContext parserContext) {
|
||||
ManagedList<Object> list = new ManagedList<Object>();
|
||||
list.setSource(parserContext.extractSource(parentElement));
|
||||
@@ -620,27 +612,6 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
return list;
|
||||
}
|
||||
|
||||
private ManagedList<BeanReference> extractBeanRefSubElements(Element parentElement, ParserContext parserContext){
|
||||
ManagedList<BeanReference> list = new ManagedList<BeanReference>();
|
||||
list.setSource(parserContext.extractSource(parentElement));
|
||||
for (Element refElement : DomUtils.getChildElementsByTagName(parentElement, "ref")) {
|
||||
BeanReference reference;
|
||||
if (StringUtils.hasText("bean")) {
|
||||
reference = new RuntimeBeanReference(refElement.getAttribute("bean"),false);
|
||||
list.add(reference);
|
||||
}
|
||||
else if (StringUtils.hasText("parent")){
|
||||
reference = new RuntimeBeanReference(refElement.getAttribute("parent"),true);
|
||||
list.add(reference);
|
||||
}
|
||||
else {
|
||||
parserContext.getReaderContext().error("'bean' or 'parent' attribute is required for <ref> element",
|
||||
parserContext.extractSource(parentElement));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A FactoryBean for a CompositeUriComponentsContributor that obtains the
|
||||
@@ -671,7 +642,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompositeUriComponentsContributor getObject() throws Exception {
|
||||
public CompositeUriComponentsContributor getObject() {
|
||||
return this.uriComponentsContributor;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -63,8 +63,6 @@ import org.springframework.validation.Validator;
|
||||
import org.springframework.web.HttpRequestHandler;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.WebDataBinder;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
@@ -134,10 +132,10 @@ import org.springframework.web.util.UrlPathHelper;
|
||||
* <p>Registers a {@link HandlerExceptionResolverComposite} with this chain of
|
||||
* exception resolvers:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions
|
||||
* through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated
|
||||
* with @{@link ResponseStatus}.
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
|
||||
* {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring
|
||||
* exception types
|
||||
* </ul>
|
||||
@@ -865,12 +863,11 @@ public class WebMvcConfigurationSupport implements ApplicationContextAware, Serv
|
||||
* A method available to subclasses for adding default {@link HandlerExceptionResolver}s.
|
||||
* <p>Adds the following exception resolvers:
|
||||
* <ul>
|
||||
* <li>{@link ExceptionHandlerExceptionResolver}
|
||||
* for handling exceptions through @{@link ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver}
|
||||
* for exceptions annotated with @{@link ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver}
|
||||
* for resolving known Spring exception types
|
||||
* <li>{@link ExceptionHandlerExceptionResolver} for handling exceptions through
|
||||
* {@link org.springframework.web.bind.annotation.ExceptionHandler} methods.
|
||||
* <li>{@link ResponseStatusExceptionResolver} for exceptions annotated with
|
||||
* {@link org.springframework.web.bind.annotation.ResponseStatus}.
|
||||
* <li>{@link DefaultHandlerExceptionResolver} for resolving known Spring exception types
|
||||
* </ul>
|
||||
*/
|
||||
protected final void addDefaultHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
|
||||
|
||||
@@ -3975,11 +3975,11 @@ especially when relying on `@ResponseBody` methods rather than on view resolutio
|
||||
may be more convenient to directly set the status of the response and optionally write
|
||||
error content to the body of the response.
|
||||
|
||||
You can do that with `@ExceptionHandler` methods. When declared within a controller such
|
||||
methods apply to exceptions raised by `@RequestMapping` methods of that controller (or
|
||||
any of its subclasses). You can also declare an `@ExceptionHandler` method within an
|
||||
`@ControllerAdvice` class in which case it handles exceptions from `@RequestMapping`
|
||||
methods from many controllers. Below is an example of a controller-local
|
||||
You can do that with `@ExceptionHandler` methods. When declared within a controller,
|
||||
such methods apply to exceptions raised by `@RequestMapping` methods of that controller
|
||||
(or any of its subclasses). You can also declare an `@ExceptionHandler` method within
|
||||
an `@ControllerAdvice` class in which case it handles exceptions from `@RequestMapping`
|
||||
methods from many controllers. Below is an example for a controller-local
|
||||
`@ExceptionHandler` method:
|
||||
|
||||
[source,java,indent=0]
|
||||
@@ -3988,21 +3988,28 @@ methods from many controllers. Below is an example of a controller-local
|
||||
@Controller
|
||||
public class SimpleController {
|
||||
|
||||
// @RequestMapping methods omitted ...
|
||||
// ...
|
||||
|
||||
@ExceptionHandler(IOException.class)
|
||||
public ResponseEntity<String> handleIOException(IOException ex) {
|
||||
// prepare responseEntity
|
||||
return responseEntity;
|
||||
@ExceptionHandler
|
||||
public ResponseEntity<String> handle(IOException ex) {
|
||||
// ...
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
The `@ExceptionHandler` value can be set to an array of Exception types. If an exception
|
||||
is thrown that matches one of the types in the list, then the method annotated with the
|
||||
matching `@ExceptionHandler` will be invoked. If the annotation value is not set then
|
||||
the exception types listed as method arguments are used.
|
||||
The exception may match against a top-level exception being propagated (i.e. a direct
|
||||
`IOException` thrown), or against the immediate cause within a top-level wrapper exception
|
||||
(e.g. an `IOException` wrapped inside an `IllegalStateException`).
|
||||
|
||||
For matching exception types, preferably 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 specifically, the `ExceptionDepthComparator` is
|
||||
used to sort exceptions based on their depth from the thrown exception type.
|
||||
|
||||
Alternatively, the `@ExceptionHandler` value can be set to an array of exception types.
|
||||
If an exception is thrown that matches one of the types in the list, then the method
|
||||
annotated with the matching `@ExceptionHandler` will be invoked. If the annotation value
|
||||
is not set, then the declared method parameter type will be used for matching.
|
||||
|
||||
[TIP]
|
||||
====
|
||||
@@ -4014,18 +4021,26 @@ advice bean. As a consequence, when using a multi-advice arrangement, please dec
|
||||
primary root exception mappings on a prioritized advice bean with a corresponding order!
|
||||
====
|
||||
|
||||
Much like standard controller methods annotated with a `@RequestMapping` annotation, the
|
||||
method arguments and return values of `@ExceptionHandler` methods can be flexible. For
|
||||
example, the `HttpServletRequest` can be accessed in Servlet environments and the
|
||||
Much like standard controller methods annotated with a `@RequestMapping` annotation,
|
||||
the method arguments and return values of `@ExceptionHandler` methods can be flexible.
|
||||
For example, the `HttpServletRequest` can be accessed in Servlet environments and the
|
||||
`PortletRequest` in Portlet environments. The return type can be a `String`, which is
|
||||
interpreted as a view name, a `ModelAndView` object, a `ResponseEntity`, or you can also
|
||||
add the `@ResponseBody` to have the method return value converted with message
|
||||
converters and written to the response stream.
|
||||
|
||||
Last but not least, an `@ExceptionHandler` method implementation may choose to back
|
||||
out of dealing with a given exception instance by rethrowing it in its original form.
|
||||
This is useful in scenarios where you are only interested in root-level matches or in
|
||||
matches within a specific context that cannot be statically determined. A rethrown
|
||||
exception will be propagated through the remaining resolution chain, just like if
|
||||
the given `@ExceptionHandler` method would not have matched in the first place.
|
||||
|
||||
|
||||
|
||||
[[mvc-ann-rest-spring-mvc-exceptions]]
|
||||
=== Handling Standard Spring MVC Exceptions
|
||||
|
||||
Spring MVC may raise a number of exceptions while processing a request. The
|
||||
`SimpleMappingExceptionResolver` can easily map any exception to a default error view as
|
||||
needed. However, when working with clients that interpret responses in an automated way
|
||||
|
||||
Reference in New Issue
Block a user