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.

Architect's Guide
Architect's Guide
Architect's Guide
Elegant Backend API Design: Unified Response Wrapper with Spring Boot Annotations & AOP

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.

Message enum design
Message enum design

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.

Result class
Result class

Controller Layer

Initially, controllers manually wrap return values using the Result constructor:

Controller example
Controller example

This works but clutters business logic with repetitive wrapping code.

Beauty Optimization: Static Factory Methods

Adding static factory methods to Result simplifies the controller:

Result static methods
Result static methods

Refactored controller becomes cleaner:

Refactored controller
Refactored controller

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.

Ideal controller returning domain object
Ideal controller returning domain object

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

@ResponseResult

is a simple marker annotation placed on controller methods or classes.

@ResponseResult annotation
@ResponseResult annotation

Interceptor

The interceptor resolves the handler method, checks for @ResponseResult, and stores a boolean flag in the request attributes.

Interceptor implementation
Interceptor implementation

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.

ResponseBodyAdvice implementation
ResponseBodyAdvice implementation

Exception handling snippet:

Exception handling in ResponseBodyAdvice
Exception handling in ResponseBodyAdvice

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.

Controller with @ResponseResult
Controller with @ResponseResult

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootAPI DesignCustom AnnotationRESTful APIResponse WrapperControllerAdviceResponseBodyAdviceError Code Design
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

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.