Backend Development 18 min read

Unified Parameter Validation, Response Wrapping, and Exception Handling in Spring Boot Controllers

This article explains how to handle controller parameters, implement unified status codes with ResultVo, apply @Validated for request validation, and use @RestControllerAdvice together with ResponseBodyAdvice to automatically wrap responses and centralize exception handling in a Spring Boot backend.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Unified Parameter Validation, Response Wrapping, and Exception Handling in Spring Boot Controllers

The article introduces the four parts of a complete backend request—URL, HTTP method, request data, and response data—and outlines three problems: elegant parameter validation, unified response formatting, and exception handling.

1. Controller Parameter Reception

Shows basic @RestController, @RequestMapping, @GetMapping and @PostMapping usage with a sample ProductInfoController and explains how request bodies are automatically mapped to VO objects.

@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

Defines a ResultVo wrapper that adds a numeric code , a message msg , and a data field to every response. The article recommends using an enum implementing a StatusCode interface instead of hard‑coding numbers.

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 int code;
    private 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.code = statusCode.getCode(); this.msg = statusCode.getMsg(); this.data = null; }
}

3. Unified Validation

Uses Bean Validation annotations (@NotNull, @Min) on VO fields and the @Validated annotation on controller methods. When validation fails, Spring throws BindException.

public class ProductInfoVo {
    @NotNull(message = "商品名称不允许为空")
    private String productName;
    @Min(value = 0, message = "商品价格不允许为负数")
    private BigDecimal productPrice;
    private Integer productStatus;
}

Example of a failed validation response (400 Bad Request) is shown.

4. Centralized Exception Handling

A @RestControllerAdvice class catches BindException and wraps it into a ResultVo with ResultCode.VALIDATE_ERROR . It also catches custom APIException to return its code, msg, and detailed message.

@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());
    }
    @ExceptionHandler(APIException.class)
    public ResultVo handleAPIException(APIException e) {
        return new ResultVo(e.getCode(), e.getMsg(), e.getMessage());
    }
}

5. Unified Response Advice

Implements ResponseBodyAdvice to automatically wrap any non‑ResultVo response into ResultVo, except for String returns (which are converted to JSON) and methods annotated with a custom @NotControllerResponseAdvice.

@RestControllerAdvice(basePackages = {"com.bugpool.leilema"})
public class ControllerResponseAdvice implements ResponseBodyAdvice
{
    @Override
    public boolean supports(MethodParameter methodParameter, Class
> aClass) {
        return !(methodParameter.getParameterType().isAssignableFrom(ResultVo.class)
                 || methodParameter.hasMethodAnnotation(NotControllerResponseAdvice.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);
    }
}

A custom annotation @NotControllerResponseAdvice can be placed on methods (e.g., health check) to skip the automatic wrapping.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {}

6. Business Exception Standardization

Defines AppCode enum for business errors, APIException class that carries a status code and message, and adds handling in ControllerExceptionAdvice to return a consistent JSON structure.

public enum AppCode implements StatusCode {
    APP_ERROR(2000, "业务异常"),
    PRICE_ERROR(2001, "价格异常");
    private int code; private String msg; /* constructor omitted */
}
@Getter
public class APIException extends RuntimeException {
    private int code; private 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(); }
}

Finally, throwing new APIException(AppCode.ORDER_NOT_EXIST, "订单号不存在:" + orderId) results in a JSON response with code 2003, msg "订单不存在", and detailed data.

{ "code":2003, "msg":"订单不存在", "data":"订单号不存在:1998" }

The article concludes that with these patterns developers can write clean controller code, let the framework handle validation and error formatting, and keep front‑end contracts stable.

JavaValidationSpringBootControllerExceptionHandlingResponseWrapper
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.