Backend Development 10 min read

Master Spring MVC Exception Handling: Local, Global, and REST API Strategies

This article explains how to use @ExceptionHandler with @Controller, @ControllerAdvice, and @RestControllerAdvice for local and global exception handling in Spring MVC, details supported method parameters and return types, and shows how to customize REST API error responses by extending ResponseEntityExceptionHandler.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring MVC Exception Handling: Local, Global, and REST API Strategies

Environment: Spring 5.3.23

Review

@Controller and @ControllerAdvice can use @ExceptionHandler to handle controller method exceptions, as shown below:

Local exception handling

<code>@Controller
public class SimpleController {
  // ...
  // Can only handle exceptions occurring in this SimpleController
  @ExceptionHandler
  public ResponseEntity<String> handle(Exception ex) {
    // ...
  }
}
</code>

Global exception handling

<code>@RestControllerAdvice
public class TestControllerAdvice {
  // Global exception handling
  @ExceptionHandler
  public Object handle(Exception e) {
    return "Global exception: " + e.getMessage();
  }
}
</code>

Specify exception types

<code>@ExceptionHandler({FileSystemException.class, RemoteException.class})
public ResponseEntity<String> handle(Exception ex) {
  // ...
}
</code>

In @ExceptionHandler you specify which exception classes it can handle.

Exception handler parameters

@ExceptionHandler methods support the following parameters:

Exception type : Used to access the exception that was thrown.

HandlerMethod : Used to access the controller method that threw the exception.

WebRequest, NativeWebRequest : General access to request parameters, request and session attributes without using the Servlet API directly.

javax.servlet.ServletRequest, javax.servlet.ServletResponse : Choose any specific request or response type (e.g., ServletRequest, HttpServletRequest, Spring's MultipartRequest, MultipartHttpServletRequest).

HttpSession : Forces the existence of a session; never null. Note that session access is not thread‑safe; consider setting the synchronizeOnSession flag to true on RequestMappingHandlerAdapter for concurrent access.

java.security.Principal : The currently authenticated user—if known, may be a specific Principal implementation.

HttpMethod : The HTTP method of the request.

java.util.Locale : The current request locale resolved by the most specific LocaleResolver or LocaleContextResolver.

java.util.TimeZone, java.time.ZoneId : The time zone associated with the current request, determined by LocaleContextResolver.

java.io.OutputStream, java.io.Writer : Access to the raw response body exposed by the Servlet API.

java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap : Access to the model for the error response; always empty.

RedirectAttributes : Attributes used in a redirect (appended to the query string) and temporary flash attributes stored until the next request.

@SessionAttribute : Access to any session attribute, as opposed to model attributes stored in the session via @SessionAttributes.

@RequestAttribute : Access to request attributes.

Exception handler return values

@ExceptionHandler methods support the following return values:

@ResponseBody : The return value is converted by an HttpMessageConverter and written to the response.

HttpEntity<B>, ResponseEntity<B> : The return value specifies a full response (headers and body) that is converted by an HttpMessageConverter.

String : View name resolved by a ViewResolver and used with the implicit model.

View : A View instance used to render the implicit model.

java.util.Map, org.springframework.ui.Model : Attributes added to the implicit model; view name is determined implicitly by RequestToViewNameTranslator.

@ModelAttribute : Attributes added to the model; view name is determined implicitly. Note that @ModelAttribute is optional.

ModelAndView : The view and model attributes (and optional response status) to use.

void : Considered to have fully handled the response if the method also has ServletResponse, OutputStream parameters or @ResponseStatus. Otherwise, void can represent a “no response body” for REST controllers or default view name selection for HTML controllers.

Any other return value : If it does not match any of the above and is not a simple type, it is treated as a model attribute to be added to the model; simple types remain unresolved.

REST API Exceptions

A common requirement for REST services is to include error details in the response body. Spring does not do this automatically; the details are application‑specific. A @RestController can use an @ExceptionHandler method that returns a ResponseEntity to set the status and body. Such methods can also be declared in a @ControllerAdvice class for global application.

To implement global exception handling with error details, extend ResponseEntityExceptionHandler, which provides handling for Spring MVC‑thrown exceptions and hooks for custom response bodies. Create a subclass annotated with @ControllerAdvice and override the necessary methods, for example:

<code>@RestControllerAdvice
static class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
  @Override
  protected ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
    System.out.println(">>>>>>>>>> - body - " + body);
    System.out.println(">>>>>>>>>> - headers - " + headers);
    System.out.println(">>>>>>>>>> - status - " + status);
    return new ResponseEntity<>(ex.getMessage(), headers, status);
  }
}
</code>

ResponseEntityExceptionHandler already defines handlers for many exception types, such as HttpRequestMethodNotSupportedException, HttpMediaTypeNotSupportedException, MissingPathVariableException, etc.

<code>public abstract class ResponseEntityExceptionHandler {
  @ExceptionHandler({
    HttpRequestMethodNotSupportedException.class,
    HttpMediaTypeNotSupportedException.class,
    HttpMediaTypeNotAcceptableException.class,
    MissingPathVariableException.class,
    MissingServletRequestParameterException.class,
    ServletRequestBindingException.class,
    ConversionNotSupportedException.class,
    TypeMismatchException.class,
    HttpMessageNotReadableException.class,
    HttpMessageNotWritableException.class,
    MethodArgumentNotValidException.class,
    MissingServletRequestPartException.class,
    BindException.class,
    NoHandlerFoundException.class,
    AsyncRequestTimeoutException.class
  })
  @Nullable
  public final ResponseEntity<Object> handleException(Exception ex, WebRequest request) throws Exception {
    HttpHeaders headers = new HttpHeaders();
    if (ex instanceof HttpRequestMethodNotSupportedException) {
      HttpStatus status = HttpStatus.METHOD_NOT_ALLOWED;
      return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, headers, status, request);
    } else if (ex instanceof HttpMediaTypeNotSupportedException) {
      HttpStatus status = HttpStatus.UNSUPPORTED_MEDIA_TYPE;
      return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, headers, status, request);
    } // ...
    else {
      throw ex;
    }
  }
}
</code>

Done!!!

Follow me so I don't get lost.

Javabackend developmentException HandlingREST APISpring MVCControllerAdvice
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.