Backend Development 11 min read

Improving Spring Boot Controllers: From Unclean to Elegant with Validation and Global Exception Handling

This article examines common pitfalls in Spring Boot controller implementations, demonstrates how excessive if‑else checks and business logic in controllers degrade code quality, and provides clean, refactored examples using @Valid validation, concise error handling, and a global exception handler to achieve more maintainable backend services.

Top Architect
Top Architect
Top Architect
Improving Spring Boot Controllers: From Unclean to Elegant with Validation and Global Exception Handling

Many developers encounter controllers that are overloaded with repetitive if checks, try‑catch blocks, and business logic, leading to unreadable and hard‑to‑maintain code. The author shares personal observations of such "un‑elegant" controllers and explains why they raise blood pressure.

Un‑elegant Controller Example

@RestController
@RequestMapping("/user/test")
public class UserController {
    private static Logger logger = LoggerFactory.getLogger(UserController.class);
    @Autowired
    private UserService userService;
    @Autowired
    private AuthService authService;
    @PostMapping
    public CommonResult userRegistration(@RequestBody UserVo userVo) {
        if (StringUtils.isBlank(userVo.getUsername())) {
            return CommonResult.error("用户名不能为空");
        }
        if (StringUtils.isBlank(userVo.getPassword())) {
            return CommonResult.error("密码不能为空");
        }
        logger.info("注册用户:{}", userVo.getUsername());
        try {
            userService.registerUser(userVo.getUsername());
            return CommonResult.ok();
        } catch (Exception e) {
            logger.error("注册用户失败:{}", userVo.getUsername(), e);
            return CommonResult.error("注册失败");
        }
    }
    @PostMapping("/login")
    @PermitAll
    @ApiOperation("使用账号密码登录")
    public CommonResult
login(@RequestBody AuthLoginReqVO reqVO) {
        if (StringUtils.isBlank(reqVO.getUsername())) {
            return CommonResult.error("用户名不能为空");
        }
        if (StringUtils.isBlank(reqVO.getPassword())) {
            return CommonResult.error("密码不能为空");
        }
        try {
            return success(authService.login(reqVO));
        } catch (Exception e) {
            logger.error("注册用户失败:{}", reqVO.getUsername(), e);
            return CommonResult.error("注册失败");
        }
    }
}

This version contains duplicated validation logic, verbose logging, and nested try‑catch blocks, roughly doubling the line count compared to a cleaner approach.

Elegant Controller Refactor

@RestController
@RequestMapping("/user/test")
public class UserController1 {
    private static Logger logger = LoggerFactory.getLogger(UserController1.class);
    @Autowired
    private UserService userService;
    @Autowired
    private AuthService authService;
    @PostMapping("/userRegistration")
    public CommonResult userRegistration(@RequestBody @Valid UserVo userVo) {
        userService.registerUser(userVo.getUsername());
        return CommonResult.ok();
    }
    @PostMapping("/login")
    @PermitAll
    @ApiOperation("使用账号密码登录")
    public CommonResult
login(@RequestBody @Valid AuthLoginReqVO reqVO) {
        return success(authService.login(reqVO));
    }
}

By moving validation to the DTO layer with @Valid and simplifying the controller methods, the code size is cut roughly in half and responsibilities are clearly separated.

Improved Validation Strategy

Replace manual if checks with declarative validation annotations such as @NotEmpty , @Length , and @Pattern on the request objects. Example DTO:

@ApiModel(value = "管理后台 - 账号密码登录 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO {
    @ApiModelProperty(value = "账号", required = true, example = "user")
    @NotEmpty(message = "登录账号不能为空")
    @Length(min = 4, max = 16, message = "账号长度为 4-16 位")
    @Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
    private String username;

    @ApiModelProperty(value = "密码", required = true, example = "password")
    @NotEmpty(message = "密码不能为空")
    @Length(min = 4, max = 16, message = "密码长度为 4-16 位")
    private String password;
}

Spring automatically validates these fields before the controller logic runs, reducing boilerplate and improving readability.

Global Exception Handling

@ResponseBody
@RestControllerAdvice
public class ExceptionHandlerAdvice {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public CommonResult
handleValidationExceptions(MethodArgumentNotValidException ex) {
        logger.error("[handleValidationExceptions]", ex);
        StringBuilder sb = new StringBuilder();
        ex.getBindingResult().getAllErrors().forEach(error -> {
            String fieldName = ((org.springframework.validation.FieldError) error).getField();
            String errorMessage = error.getDefaultMessage();
            sb.append(fieldName).append(":").append(errorMessage).append(";");
        });
        return CommonResult.error(sb.toString());
    }

    @ExceptionHandler(Exception.class)
    public CommonResult
defaultExceptionHandler(Throwable ex) {
        logger.error("[defaultExceptionHandler]", ex);
        return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
    }
}

This advice captures validation failures and any uncaught exceptions, returning a consistent error response and keeping controller code clean.

Conclusion

Keeping controllers thin, delegating validation to DTOs with @Valid , and handling errors globally leads to more maintainable Spring Boot applications. Following these practices reduces line count, eliminates repetitive checks, and aligns with the principle that each method should focus on a single responsibility.

Javaexception handlingvalidationSpring BootController Design
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn 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.