How to Standardize Spring Boot REST API Responses with ResponseBodyAdvice
This guide explains how to use Spring's ResponseBodyAdvice to create a unified response wrapper for RESTful APIs, improving consistency, readability, and extensibility while handling common pitfalls such as String return type errors.
Environment: SpringBoot 2.7.12
1. Introduction
If you want a way to standardize the return values of your RESTful API, this article shows how to use Spring's ResponseBodyAdvice to improve API design and implementation.
Why unify interface return values?
Uniform specification: using ResponseBodyAdvice you can wrap all interface return values, making them clearer and easier to understand.
Readability improvement: the wrapper can add fields such as status code and message.
Extensibility: ResponseBodyAdvice allows you to change the format without modifying existing controller code.
2. Development Process
Define a unified response wrapper
<code>public class R {
private Integer code;
private Object data;
private String message;
public R(Integer code, Object data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public static R success(Object data) {
return new R(200, data, "success");
}
public static R failure(String message) {
return new R(500, null, message);
}
}
</code>Custom ResponseBodyAdvice
<code>@RestControllerAdvice
public class PackResponseBodyAdvice implements ResponseBodyAdvice<Object> {
@Resource
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// only process when return type is not R
return !returnType.getParameterType().equals(R.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
// unified response handling
return R.success(body);
}
}
</code>Test controller
<code>@RestController
@RequestMapping("/advices")
public class AdviceController {
@GetMapping("/str")
public String str() {
return "success";
}
@GetMapping("/{id}")
public User body(@PathVariable("id") Long id) {
return new User(id, "张三 - " + new Random().nextInt(1000));
}
}
</code>When calling /advices/{id} the final response is wrapped correctly. Calling /advices/str initially caused a ClassCastException because the String result was converted after the advice.
Fix: detect String return type and serialize the wrapper manually.
<code>public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof String) {
try {
return this.objectMapper.writeValueAsString(R.success(body));
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return R.success(body);
}
</code>Result: String responses are handled without error.
Exclude specific endpoints
<code>@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NoR {}
</code> <code>public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
// method or class without NoR annotation
return (!returnType.hasMethodAnnotation(NoR.class)
|| AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), NoR.class))
&& !returnType.getParameterType().equals(R.class);
}
</code>By adding @NoR to a controller or method you can prevent the advice from wrapping its return value.
Using a custom ResponseBodyAdvice enables unified API responses, improving readability, extensibility, and error handling while keeping the code clean and consistent.
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.
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.