Implementing Unified User Login, Exception Handling, and Data Formatting in Spring Boot
This article demonstrates how to consolidate user login authentication, exception handling, and response formatting in a Spring Boot application by using AOP, custom HandlerInterceptors, @ControllerAdvice, and a unified AjaxResult wrapper to simplify development and improve maintainability.
Introduction
The tutorial introduces three goals for a Spring Boot module: unified user login permission verification, unified data format return, and unified exception handling.
1. User Login Permission Validation
1.1 Initial login verification
Each controller method previously performed its own session check, leading to duplicated code and higher maintenance cost.
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/m1")
public Object method(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
return true; // already logged in
} else {
return false; // not logged in
}
}
// other methods ...
}The repeated checks motivate the use of a common AOP solution.
1.2 Spring AOP for login verification
AOP can apply a @Before or @Around advice to all controller methods, but it cannot easily obtain the HttpSession and cannot exclude specific endpoints such as login or registration.
1.3 Spring HandlerInterceptor
Spring provides HandlerInterceptor which offers preHandle (executed before the controller) and can be configured to include or exclude URL patterns.
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute("userinfo") != null) {
return true; // allow request
}
response.setStatus(401);
return false; // block request
}
}The interceptor returns true to continue processing or false to stop.
1.3.1 Registering the interceptor
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/user/login", "/user/reg");
}
}Alternatively, the interceptor can be added directly with new LoginInterceptor() and a list of excluded patterns can be built programmatically.
2. Unified Exception Handling
Using @ControllerAdvice together with @ExceptionHandler allows central handling of all exceptions.
@ControllerAdvice
public class ErrorAdvice {
@ExceptionHandler(Exception.class)
@ResponseBody
public HashMap
handleAll(Exception e) {
HashMap
result = new HashMap<>();
result.put("code", "-1");
result.put("msg", e.getMessage());
return result;
}
@ExceptionHandler(ArithmeticException.class)
@ResponseBody
public HashMap
handleArithmetic(ArithmeticException e) {
HashMap
result = new HashMap<>();
result.put("code", "-2");
result.put("msg", e.getMessage());
return result;
}
}Specific exception handlers can be added for finer‑grained responses, e.g., handling NullPointerException separately.
3. Unified Data Return Format
3.1 Why unify the response?
Consistent structure simplifies front‑end parsing.
Reduces communication overhead between front‑end and back‑end.
Facilitates centralized maintenance and standardization.
3.2 Implementation with ResponseBodyAdvice
Implement ResponseBodyAdvice to wrap every controller return value into a standard JSON object.
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice
{
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true; // apply to all responses
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof HashMap) {
return body; // already wrapped
}
if (body instanceof String) {
return new ObjectMapper().writeValueAsString(AjaxResult.success(body));
}
return AjaxResult.success(body);
}
}The helper class AjaxResult provides static methods success and fail to generate the standard map.
public class AjaxResult {
public static HashMap
success(Object data) {
HashMap
r = new HashMap<>();
r.put("code", 200);
r.put("msg", "");
r.put("data", data);
return r;
}
public static HashMap
fail(int code, String msg) {
HashMap
r = new HashMap<>();
r.put("code", code);
r.put("msg", msg);
r.put("data", "");
return r;
}
}When a controller returns a String , the advice converts the wrapped result back to JSON string to avoid type‑mismatch errors.
3.3 Source code analysis of @ControllerAdvice
The article also briefly explains how Spring registers @ControllerAdvice beans during container initialization, linking them to RequestMappingHandlerAdapter and the internal cache that triggers the advice on relevant events.
Overall, the tutorial shows a complete workflow for centralizing authentication, error handling, and response formatting in a Spring Boot project, improving code reuse and project consistency.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.