Backend Development 18 min read

Unified Controller Layer Handling, Validation, and Exception Management in Spring Boot

This article explains how to structure a Spring Boot controller layer by separating URL, request method, request data, and response data, then introduces unified status‑code packaging with ResultVo, parameter validation using @Validated, and centralized exception handling with @RestControllerAdvice and AOP to produce consistent API responses.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Unified Controller Layer Handling, Validation, and Exception Management in Spring Boot

Introduction

The article focuses on the controller layer in a Spring Boot backend, describing the four parts of a request (URL, method, request data, response data) and the three problems it solves: elegant parameter validation, unified response handling, and exception processing.

1. Controller Parameter Reception

Typical GET and POST mappings are shown with a sample controller:

@RestController
@RequestMapping("/product/product-info")
public class ProductInfoController {
    @Autowired
    ProductInfoService productInfoService;

    @GetMapping("/findById")
    public ProductInfoQueryVo findById(Integer id) {
        // ...
    }

    @PostMapping("/page")
    public IPage findPage(Page page, ProductInfoQueryVo vo) {
        // ...
    }
}

2. Unified Status Code

To cooperate with the frontend, responses are wrapped in a standard format containing code , msg , and data . Example of raw data vs. wrapped data is provided.

public interface StatusCode {
    int getCode();
    String getMsg();
}

@Getter
public enum ResultCode implements StatusCode {
    SUCCESS(1000, "请求成功"),
    FAILED(1001, "请求失败"),
    VALIDATE_ERROR(1002, "参数校验失败"),
    RESPONSE_PACK_ERROR(1003, "response返回包装失败");
    private final int code;
    private final String msg;
    ResultCode(int code, String msg) { this.code = code; this.msg = msg; }
}

@Data
public class ResultVo {
    private int code;
    private String msg;
    private Object data;
    public ResultVo(Object data) { this.code = ResultCode.SUCCESS.getCode(); this.msg = ResultCode.SUCCESS.getMsg(); this.data = data; }
    public ResultVo(StatusCode statusCode, Object data) { this.code = statusCode.getCode(); this.msg = statusCode.getMsg(); this.data = data; }
    public ResultVo(StatusCode statusCode) { this(statusCode, null); }
}

2.1 Validation with @Validated

Using Bean Validation annotations on a VO class eliminates manual if‑checks.

@Data
public class ProductInfoVo {
    @NotNull(message = "商品名称不允许为空")
    private String productName;

    @Min(value = 0, message = "商品价格不允许为负数")
    private BigDecimal productPrice;
    private Integer productStatus;
}

The controller method simply receives @Validated ProductInfoVo vo and returns a ResultVo .

2.2 Unified Exception Handling

BindException is intercepted and transformed into a ResultVo with ResultCode.VALIDATE_ERROR :

@RestControllerAdvice
public class ControllerExceptionAdvice {
    @ExceptionHandler(BindException.class)
    public ResultVo handleBindException(BindException e) {
        ObjectError error = e.getBindingResult().getAllErrors().get(0);
        return new ResultVo(ResultCode.VALIDATE_ERROR, error.getDefaultMessage());
    }
}

3. Unified Response

ResponseBodyAdvice is used to automatically wrap non‑ResultVo return values. String responses are specially handled to avoid JSON conversion errors.

@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice
{
    @Override
    public boolean supports(MethodParameter methodParameter, Class
> aClass) {
        return !methodParameter.getParameterType().isAssignableFrom(ResultVo.class);
    }
    @Override
    public Object beforeBodyWrite(Object data, MethodParameter returnType, MediaType mediaType,
                                Class
> aClass,
                                ServerHttpRequest request, ServerHttpResponse response) {
        if (returnType.getGenericParameterType().equals(String.class)) {
            ObjectMapper mapper = new ObjectMapper();
            try { return mapper.writeValueAsString(new ResultVo(data)); }
            catch (JsonProcessingException e) { throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage()); }
        }
        return new ResultVo(data);
    }
}

An annotation @NotControllerResponseAdvice can be added to methods that should bypass the wrapper (e.g., health checks).

4. Unified Business Exception

Custom business exceptions extend APIException and carry a StatusCode (e.g., AppCode.ORDER_NOT_EXIST ).

@Getter
public enum AppCode implements StatusCode {
    APP_ERROR(2000, "业务异常"),
    PRICE_ERROR(2001, "价格异常"),
    ORDER_NOT_EXIST(2003, "订单不存在");
    private final int code;
    private final String msg;
    AppCode(int code, String msg) { this.code = code; this.msg = msg; }
}

public class APIException extends RuntimeException {
    private final int code;
    private final String msg;
    public APIException(StatusCode statusCode, String message) { super(message); this.code = statusCode.getCode(); this.msg = statusCode.getMsg(); }
    public APIException(String message) { super(message); this.code = AppCode.APP_ERROR.getCode(); this.msg = AppCode.APP_ERROR.getMsg(); }
}

The same @RestControllerAdvice catches APIException and returns a standardized ResultVo .

Conclusion

By defining a unified response wrapper, validation annotations, and centralized exception handling, backend developers can write cleaner controller code, ensure consistent API contracts, and simplify frontend integration.

Exception HandlingValidationSpring BootControllerResultVo
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

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.