Standardizing Spring Boot Controller Layer: Parameter Reception, Unified Status Codes, Validation, Response Wrapping, and Exception Handling
This article provides a comprehensive guide to standardizing the Spring Boot controller layer by explaining how to receive parameters, define unified status codes, apply validation annotations, wrap responses consistently, and handle exceptions globally, all illustrated with practical Java code examples.
The article introduces a systematic approach to handling the controller layer in a Spring Boot backend, focusing on four main aspects: request parameter reception, unified status codes, validation, response wrapping, and exception handling.
1. Controller Parameter Reception – Shows how to use @RestController , @RequestMapping , @GetMapping , and @PostMapping to map URLs and bind JSON payloads 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) {
...
}
}Key annotations are explained: @RestController = @Controller + @ResponseBody , @RequestMapping defines a common URL prefix, and @GetMapping / @PostMapping specify HTTP methods.
2. Unified Status Codes – Proposes wrapping all responses in a standard JSON structure containing code , msg , and data . An enum ResultCode implements a StatusCode interface to centralize code definitions.
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; }
}The ResultVo class provides constructors for different scenarios (data only, custom status, etc.).
@Data
public class ResultVo {
private int code;
private String msg;
private Object data;
public ResultVo(Object data) { /* use SUCCESS */ }
public ResultVo(StatusCode statusCode, Object data) { /* custom */ }
public ResultVo(StatusCode statusCode) { /* no data */ }
}3. Unified Validation – Demonstrates using @Validated on controller methods and constraint annotations such as @NotNull and @Min on VO fields. Validation errors are captured and transformed into the unified response format.
@Data
public class ProductInfoVo {
@NotNull(message = "商品名称不允许为空")
private String productName;
@Min(value = 0, message = "商品价格不允许为负数")
private BigDecimal productPrice;
private Integer productStatus;
}
@PostMapping("/findByVo")
public ProductInfo findByVo(@Validated ProductInfoVo vo) {
// business logic
return productInfoService.getOne(new QueryWrapper(productInfo));
}When validation fails, a BindException is thrown; a @RestControllerAdvice converts it to 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());
}
}4. Unified Response – Implements a ResponseBodyAdvice that automatically wraps non‑ ResultVo return values into ResultVo . Special handling for String responses is provided.
@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)) {
try {
return new ObjectMapper().writeValueAsString(new ResultVo(data));
} catch (JsonProcessingException e) {
throw new APIException(ResultCode.RESPONSE_PACK_ERROR, e.getMessage());
}
}
return new ResultVo(data);
}
}For endpoints that must return raw data (e.g., health checks), a custom annotation @NotControllerResponseAdvice can be added to skip the wrapper.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotControllerResponseAdvice {}
@RestController
public class HealthController {
@GetMapping("/health")
@NotControllerResponseAdvice
public String health() { return "success"; }
}5. Unified Exception Handling – Defines a custom APIException that carries a status code and message, and a global advice that converts any APIException into a ResultVo . Business‑specific error codes (e.g., AppCode ) are also shown.
@Getter
public enum AppCode implements StatusCode {
APP_ERROR(2000, "业务异常"),
PRICE_ERROR(2001, "价格异常");
private final int code;
private final String msg;
AppCode(int code, String msg) { this.code = code; this.msg = msg; }
}
@Getter
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();
}
}
@RestControllerAdvice
public class ControllerExceptionAdvice {
@ExceptionHandler(APIException.class)
public ResultVo handleAPIException(APIException e) {
return new ResultVo(e.getCode(), e.getMsg(), e.getMessage());
}
}With these components, developers can write clean controller methods without repetitive boilerplate, while the system guarantees a consistent API contract and centralized error logging.
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.
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.