Zero‑Intrusion Global Response Formatting in Spring Boot with HttpMessageConverter
This guide shows how to configure Spring Boot to automatically wrap all controller responses in a unified Result object, apply global JSON formatting for dates, Long values, null handling, and custom enum serialization using HttpMessageConverter, ResponseBodyAdvice, and a global exception handler.
Step 1: Global JSON Formatting
Define a @Configuration class that replaces the default HttpMessageConverter with a custom MappingJackson2HttpMessageConverter. Register a JavaTimeModule with a LocalDateTimeSerializer using the pattern "yyyy-MM-dd HH:mm:ss". Register a SimpleModule that serializes all Long and Long.TYPE values as strings to avoid JavaScript precision loss. Set the customized ObjectMapper on the converter and add it to the list of converters.
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jsonConverter());
}
@Bean
public MappingJackson2HttpMessageConverter jsonConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
// 1. Global date format
JavaTimeModule timeModule = new JavaTimeModule();
timeModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
// 2. Long → String
SimpleModule longModule = new SimpleModule();
longModule.addSerializer(Long.class, ToStringSerializer.instance);
longModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(timeModule);
objectMapper.registerModule(longModule);
converter.setObjectMapper(objectMapper);
return converter;
}
}Step 2: Automatic Result Wrapping
Implement a @RestControllerAdvice that implements ResponseBodyAdvice<Object>. The supports method always returns true, so every response body passes through beforeBodyWrite. If the body is already a Result, it is returned unchanged; if it is a String, it is wrapped and converted to JSON to avoid conversion errors; otherwise the body is wrapped with Result.success(body).
@RestControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) {
return body;
}
if (body instanceof String) {
return Result.success(body).toString();
}
return Result.success(body);
}
}Step 3: Global Null Value Formatting
Configure the ObjectMapper to exclude null fields (JsonInclude.Include.NON_NULL). To convert null to empty strings or empty collections, a custom serializer can be added, but the article only shows the inclusion setting.
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);Step 4: Global Exception Handling
Define another @RestControllerAdvice that catches all exceptions and returns a standardized failure Result.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<?> exception(Exception e) {
return Result.fail(500, "服务器异常");
}
}Before and After Comparison
Before: controller methods must return Result<T> explicitly.
@GetMapping("/user/{id}")
public Result<User> getUser(@PathVariable Integer id) {
return Result.success(userService.getById(id));
}After: the same method can return the domain object directly; the global advice wraps it automatically.
@GetMapping("/user/{id}")
public User getUser(@PathVariable Integer id) {
return userService.getById(id);
}The JSON sent to the front‑end remains the same:
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"username": "张三"
}
}Additional effects include automatic conversion of all LocalDateTime fields to the configured pattern, all Long fields to strings (preventing JavaScript precision loss), and unified handling of null values.
Summary
By combining a custom HttpMessageConverter, a global ResponseBodyAdvice, and a global exception handler, Spring Boot projects can achieve zero‑intrusion, fully automatic, and standardized response processing: dates are uniformly formatted, long IDs are safely transmitted, nulls are consistently represented, and every endpoint returns a Result wrapper without repetitive code.
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.
Java Tech Workshop
Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.
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.
