Unified Global Response and Exception Handling in Spring Boot
This article explains how to implement a unified response format and centralized exception handling in Spring Boot applications using @RestControllerAdvice, @ControllerAdvice, ResponseBodyAdvice, and custom response entities, providing code examples and best‑practice guidelines for robust backend APIs.
Spring Boot applications often expose RESTful APIs, and a consistent response structure improves client integration, reduces validation effort, and enhances maintainability. This guide introduces a unified response entity and global exception handling strategy.
1. Introduction
Most Spring Boot services return data through RESTful endpoints. Standardizing the response format and error handling simplifies communication between front‑end and back‑end components.
2. How to Implement Unified Handling
Use @RestControllerAdvice or @ControllerAdvice annotations to create a global interceptor. The @ControllerAdvice annotation works together with @ExceptionHandler , @InitBinder , and @ModelAttribute to process exceptions, bind custom parameters, and add model attributes before controller methods execute.
Combine @ExceptionHandler with @ControllerAdvice to capture specific exceptions.
Use @InitBinder to register custom type converters (e.g., date parsing).
Apply @ModelAttribute to inject objects such as the current logged‑in user into controller methods.
Example of a global handler class:
public class GlobalHandler {
private final Logger logger = LoggerFactory.getLogger(GlobalHandler.class);
@ModelAttribute("loginUserInfo")
public UserDetails modelAttribute() {
return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
@InitBinder
protected void initBinder(WebDataBinder binder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result exceptionHandler(MethodArgumentNotValidException e) {
Result result = new Result(BizExceptionEnum.INVALID_REQ_PARAM.getErrorCode(),
BizExceptionEnum.INVALID_REQ_PARAM.getErrorMsg());
logger.error("req params error", e);
return result;
}
@ExceptionHandler(BizException.class)
public Result exceptionHandler(BizException e) {
BizExceptionEnum exceptionEnum = e.getBizExceptionEnum();
Result result = new Result(exceptionEnum.getErrorCode(), exceptionEnum.getErrorMsg());
logger.error("business error", e);
return result;
}
@ExceptionHandler(Exception.class)
public Result exceptionHandler(Exception e) {
Result result = new Result(1000, "网络繁忙,请稍后再试");
logger.error("application error", e);
return result;
}
}In a controller you can retrieve the @ModelAttribute object directly:
@PostMapping("/getExamListByOpInfo")
public Result
> getExamListByOpInfo(@NotNull Date examOpDate,
@ModelAttribute("loginUserInfo") UserDetails userDetails) {
List
resVos = examService.getExamListByOpInfo(examOpDate, userDetails);
return new Result<>(resVos);
}3. Unified Response Processing
The GlobalResponse class implements ResponseBodyAdvice to wrap any controller return value into a GlobalResponseEntity unless the response is already of that type, a file resource, or the media type is not JSON.
package com.naylor.globalresponsebody.handler.response;
import com.alibaba.fastjson.JSON;
import com.naylor.globalresponsebody.handler.GlobalResponseEntity;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice("com.naylor")
public class GlobalResponse implements ResponseBodyAdvice
{
@Override
public boolean supports(MethodParameter methodParameter, Class
> aClass) {
// TODO: filter logic
return true;
}
@Override
public Object beforeBodyWrite(Object responseObject, MethodParameter methodParameter,
MediaType mediaType, Class
> aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (responseObject == null) {
return new GlobalResponseEntity<>("55555", "response is empty.");
}
if (responseObject instanceof Resource) {
return responseObject;
}
if (methodParameter.getMethod().getReturnType().isAssignableFrom(Void.TYPE)) {
return new GlobalResponseEntity<>("55555", "response is empty.");
}
if (responseObject instanceof GlobalResponseEntity) {
return responseObject;
}
if (responseObject instanceof String) {
String responseString = JSON.toJSONString(new GlobalResponseEntity<>(responseObject));
return responseString;
}
if (!mediaType.includes(MediaType.APPLICATION_JSON)) {
return responseObject;
}
return new GlobalResponseEntity<>(responseObject);
}
}4. Unified Exception Handling
The GlobalException class, annotated with @RestControllerAdvice("com.naylor") and @ResponseBody , defines handlers for generic exceptions, 404 errors, runtime exceptions, business exceptions, and validation errors, converting them into a consistent GlobalResponseEntity payload.
@RestControllerAdvice("com.naylor")
@ResponseBody
@Slf4j
public class GlobalException {
@ExceptionHandler(Exception.class)
public ResponseEntity
handleException(Exception e) {
return new ResponseEntity<>(
new GlobalResponseEntity<>(false, "555", e.getMessage() == null ? "未知异常" : e.getMessage()),
HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(NoHandlerFoundException.class)
public ResponseEntity
handleNoHandlerFoundException(NoHandlerFoundException e) {
return new ResponseEntity<>(
new GlobalResponseEntity<>(false, "4040", e.getMessage() == null ? "请求的资源不存在" : e.getMessage()),
HttpStatus.NOT_FOUND);
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity
handleRuntimeException(RuntimeException e) {
log.error("handleRuntimeException:", e);
return new ResponseEntity<>(
new GlobalResponseEntity<>(false, "rt555", e.getMessage() == null ? "运行时异常" : e.getMessage().replace("java.lang.RuntimeException: ", "")),
HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(BizServiceException.class)
public ResponseEntity
handleBizServiceException(BizServiceException e) {
return new ResponseEntity<>(
new GlobalResponseEntity<>(false, e.getErrorCode(), e.getMessage()),
HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler({MethodArgumentNotValidException.class})
public ResponseEntity
handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String msg = "参数校验失败";
List
fieldFailedValidates = this.extractFailedMessage(e.getBindingResult().getFieldErrors());
if (fieldFailedValidates != null && !fieldFailedValidates.isEmpty()) {
msg = fieldFailedValidates.get(0).getMessage();
}
return new ResponseEntity<>(
new GlobalResponseEntity<>(false, "arg555", msg, null),
HttpStatus.BAD_REQUEST);
}
private List
extractFailedMessage(List
fieldErrors) {
List
fieldFailedValidates = new ArrayList<>();
if (fieldErrors != null && !fieldErrors.isEmpty()) {
for (FieldError fieldError : fieldErrors) {
FieldFailedValidate fv = new FieldFailedValidate();
fv.setMessage(fieldError.getDefaultMessage());
fv.setName(fieldError.getField());
fieldFailedValidates.add(fv);
}
}
return fieldFailedValidates;
}
}The GlobalResponseEntity class is a generic container with fields success , code , message , and data , providing multiple constructors and static factory methods for success and error responses.
@Data
@Accessors(chain = true)
public class GlobalResponseEntity
implements Serializable {
private Boolean success = true;
private String code = "000000";
private String message = "request successfully";
private T data;
public GlobalResponseEntity() {}
public GlobalResponseEntity(T data) { this.data = data; }
public GlobalResponseEntity(String code, String message) { this.code = code; this.message = message; this.data = null; }
public GlobalResponseEntity(String code, String message, T data) { this.code = code; this.message = message; this.data = data; }
public GlobalResponseEntity(Boolean success, String code, String message) { this.success = success; this.code = code; this.message = message; }
public GlobalResponseEntity(Boolean success, String code, String message, T data) { this.success = success; this.code = code; this.message = message; this.data = data; }
public static GlobalResponseEntity
badRequest(String code, String message) { return new GlobalResponseEntity<>(false, code, message); }
public static GlobalResponseEntity
badRequest() { return new GlobalResponseEntity<>(false, "404", "无法找到您请求的资源"); }
}By integrating these components, developers achieve a consistent API contract, centralized error handling, and easier client consumption across the entire Spring Boot project.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.