Elegant Backend API Design: Unified Response Wrapper with Spring Boot Annotations & AOP
This article walks through designing a clean, unified API response format with categorized error codes, then shows how to automate response wrapping in Spring Boot using a custom @ResponseResult annotation, a handler interceptor, and ResponseBodyAdvice so controllers return plain domain objects while the framework handles consistent JSON serialization.
Response Format
The article begins by defining a standard JSON response structure for all backend APIs:
{
#返回状态码
code:integer,
#返回信息描述
message:string,
#返回值
data:object
}This structure contains a status code, a human-readable message, and a generic data payload.
Code Status Codes
Instead of adding error codes ad-hoc during development (e.g., 101 for permission error, 102 for parameter error), the author recommends categorizing codes into ranges inspired by HTTP status codes:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误Custom application error ranges:
#1000~1999 区间表示参数错误
#2000~2999 区间表示用户错误
#3000~3999 区间表示接口异常This grouping lets frontend developers quickly identify the error category from the code alone, while the message field provides specific details.
Message
The message field works together with the code. The article suggests defining an enum that maps each code to its default message, making maintenance straightforward.
Enum example mapping codes to messages.
Data
The data payload varies per business scenario. A generic Result wrapper class is introduced to encapsulate code, message, and data.
Controller Layer
Initially, controllers manually wrap return values using the Result constructor:
This works but clutters business logic with repetitive wrapping code.
Beauty Optimization: Static Factory Methods
Adding static factory methods to Result simplifies the controller:
Refactored controller becomes cleaner:
Elegant Optimization: Annotation-Driven Automatic Wrapping
The static-method approach still has three drawbacks:
1. Every method returns a Result wrapper, obscuring the actual business return type. 2. Business code must explicitly call Result.success() or Result.failure() , adding boilerplate. 3. Manual validation checks (e.g., if (id == null) ) duplicate what Hibernate Validator can handle declaratively.
The goal: controllers return plain domain objects (e.g., Order), and the framework automatically wraps them into the standard JSON format.
Implementation Plan
Three core components achieve this:
1. Define a @ResponseResult annotation to mark methods whose return values need wrapping. 2. Create a handler interceptor that inspects the target method for the annotation and sets a request attribute flag. 3. Implement ResponseBodyAdvice combined with @ControllerAdvice to wrap the return value when the flag is present; also handle exceptions by checking if the body is an exception type.
Annotation Class
@ResponseResultis a simple marker annotation placed on controller methods or classes.
Interceptor
The interceptor resolves the handler method, checks for @ResponseResult, and stores a boolean flag in the request attributes.
Rewrite Response Body
The ResponseBodyAdvice implementation reads the flag; if true, it wraps the return value into a Result success response. For exceptions, it detects an exception body and builds an error Result.
Exception handling snippet:
Global exception handling is mentioned as a separate topic.
Rewrite Controller
With the infrastructure in place, controllers simply return domain objects and carry the @ResponseResult annotation.
Summary
The article concludes that the annotation-driven approach yields clean, elegant controllers while maintaining a unified response format. A suggested further optimization is caching the annotation resolution result to avoid reflection on every request.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.
