Refactoring Spring Controllers: Unified Responses, Validation, and Exception Handling
This article explains how to redesign Spring MVC controllers by extracting common responsibilities, defining a standard response wrapper, using ResponseBodyAdvice for automatic packaging, handling String return types, and implementing comprehensive validation and custom exception handling to produce clean, maintainable backend code.
In Spring MVC, the Controller layer is essential for handling HTTP requests, but its responsibilities often become tangled with business logic, parameter validation, and response formatting, leading to duplicated code and maintenance challenges.
Problems with Traditional Controllers
Parameter validation is tightly coupled with business code, violating the single‑responsibility principle.
Repeated exception handling across multiple services creates redundancy.
Inconsistent success and error response formats make client integration difficult.
Unified Response Structure
Define a generic response interface IResult with getCode() and getMessage(), an enum ResultEnum for common status codes, and a generic Result<T> class that holds code, message, and data. Static factory methods such as Result.success(data) and Result.failed(message) simplify creation.
Automatic Packaging with ResponseBodyAdvice
Implement ResponseAdvice that implements ResponseBodyAdvice<Object>. In supports() always return true. In beforeBodyWrite(), if the body is already a Result return it unchanged; if the body is a String, convert Result.success(body) to JSON using ObjectMapper; otherwise wrap the body with Result.success(body). This removes the need for manual wrapping in each controller.
Handling String Return Types
Because StringHttpMessageConverter processes plain strings before JSON conversion, the advice must detect String responses and manually serialize the wrapped Result to JSON, ensuring the client always receives a JSON object.
Adjusting Message Converter Order
Place MappingJackson2HttpMessageConverter before StringHttpMessageConverter in the WebMvcConfigurer to let JSON conversion take precedence, or explicitly reorder them in a configuration class.
Parameter Validation
Use JSR‑303 annotations ( @NotBlank, @Min, @Max, etc.) on DTO fields and method parameters. Annotate controller classes or methods with @Validated to trigger automatic validation. Spring’s RequestResponseBodyMethodProcessor invokes validateIfApplicable(), which delegates to Hibernate Validator. Validation failures throw MethodArgumentNotValidException (for @RequestBody) or ConstraintViolationException (for method‑level constraints).
Custom Validation Rules
Create a custom annotation (e.g., @Mobile) annotated with @Constraint(validatedBy = MobileValidator.class). Implement MobileValidator extending ConstraintValidator<Mobile, CharSequence>, providing an initialize() method to read annotation attributes and an isValid() method that checks the value against a regex. This allows domain‑specific checks without polluting business logic.
Custom Exceptions and Global Exception Handling
Define specific runtime exceptions such as ForbiddenException and BusinessException. Use a @RestControllerAdvice class ( ExceptionAdvice) with @ExceptionHandler methods to translate these exceptions into the unified Result format. Handle validation exceptions ( MethodArgumentNotValidException, ConstraintViolationException) by extracting field errors and returning Result.failed with detailed messages. A catch‑all handler for Exception ensures any unexpected error also returns a consistent JSON payload.
Resulting Benefits
Controller methods become concise, focusing solely on business delegation.
All responses share a single, predictable JSON schema.
Validation logic is declarative and decoupled from service code.
Custom exceptions provide fine‑grained error handling while the global advice guarantees uniform client responses.
By applying these refactorings, developers achieve cleaner, more maintainable backend code that adheres to the single‑responsibility principle and delivers consistent API contracts.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
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.
