Non‑Intrusive Unified JSON Response Format and Global Handling in Spring MVC
This article explains how to design a non‑intrusive unified JSON response structure for Spring MVC applications, defines the standard JSON schema, provides Java enums and generic Result classes, demonstrates usage with @ResponseResultBody and @RestControllerAdvice, and shows comprehensive exception handling techniques.
In many legacy projects the API does not have a consistent response format, leading developers to rely solely on HTTP status codes, which is insufficient for conveying business errors. To address this, a non‑intrusive solution is introduced that standardizes JSON responses across the application without modifying existing endpoints.
Define JSON format
{
"code": 200,
"message": "OK",
"data": {}
}code: response status code
message: description of the response
data: actual payload
Define Java enum for status codes
@ToString
@Getter
public enum ResultStatus {
SUCCESS(HttpStatus.OK, 200, "OK"),
BAD_REQUEST(HttpStatus.BAD_REQUEST, 400, "Bad Request"),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 500, "Internal Server Error");
private HttpStatus httpStatus;
private Integer code;
private String message;
ResultStatus(HttpStatus httpStatus, Integer code, String message) {
this.httpStatus = httpStatus;
this.code = code;
this.message = message;
}
}Define generic Result wrapper class
@Getter
@ToString
public class Result<T> {
private Integer code;
private String message;
private T data;
private Result(ResultStatus resultStatus, T data) {
this.code = resultStatus.getCode();
this.message = resultStatus.getMessage();
this.data = data;
}
public static Result<Void> success() { return new Result<>(ResultStatus.SUCCESS, null); }
public static <T> Result<T> success(T data) { return new Result<>(ResultStatus.SUCCESS, data); }
public static <T> Result<T> failure() { return new Result<>(ResultStatus.INTERNAL_SERVER_ERROR, null); }
public static <T> Result<T> failure(ResultStatus status) { return new Result<>(status, null); }
}Using static factory methods simplifies object creation compared to constructors.
Result entity test
@RestController
@RequestMapping("/hello")
public class HelloController {
private static final HashMap<String, Object> INFO;
static {
INFO = new HashMap<>();
INFO.put("name", "galaxy");
INFO.put("age", "70");
}
@GetMapping("/hello")
public Map<String, Object> hello() { return INFO; }
@GetMapping("/result")
@ResponseBody
public Result<Map<String, Object>> helloResult() { return Result.success(INFO); }
}To avoid wrapping the response repeatedly, a custom annotation @ResponseResultBody and a global advice are introduced.
Create @ResponseResultBody annotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@ResponseBody
public @interface ResponseResultBody {}Global ResponseBodyAdvice implementation
@RestControllerAdvice
public class ResponseResultBodyAdvice implements ResponseBodyAdvice<Object> {
private static final Class<? extends Annotation> ANNOTATION_TYPE = ResponseResultBody.class;
@Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ANNOTATION_TYPE)
|| returnType.hasMethodAnnotation(ANNOTATION_TYPE);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> selectedConverterType,
ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof Result) { return body; }
return Result.success(body);
}
@ExceptionHandler(Exception.class)
public final ResponseEntity<Result<?>> exceptionHandler(Exception ex, WebRequest request) {
// simplified global exception handling
return new ResponseEntity<>(Result.failure(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}Controllers annotated with @ResponseResultBody automatically have their return values wrapped into the unified Result structure, and exceptions are transformed into a consistent error response.
Finally, the article lists several reference blogs and a collection of interview questions for further study.
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.
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.
