Mastering Unified API Responses, Exception Handling, and Logging in Spring Boot
This article explains how to design a standardized JSON response format, implement a unified result class and enum, handle global exceptions with @ControllerAdvice, and configure comprehensive Logback logging for Spring Boot applications, providing complete code examples and best‑practice guidelines.
Unified Result Return
Most front‑end and back‑end data exchanges use JSON, so defining a unified data format benefits interaction and UI display.
General Form of Unified Result
Whether the response is successful;
Response status code;
Status code description;
Response data;
Other identifiers.
Result Enum
@Getter
public enum ResultCodeEnum {
SUCCESS(true,20000,"成功"),
UNKNOWN_ERROR(false,20001,"未知错误"),
PARAM_ERROR(false,20002,"参数错误");
private Boolean success;
private Integer code;
private String message;
ResultCodeEnum(boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
}Unified Result Class
@Data
public class R {
private Boolean success;
private Integer code;
private String message;
private Map<String, Object> data = new HashMap<>();
private R() {}
public static R ok() {
R r = new R();
r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());
r.setCode(ResultCodeEnum.SUCCESS.getCode());
r.setMessage(ResultCodeEnum.SUCCESS.getMessage());
return r;
}
public static R error() {
R r = new R();
r.setSuccess(ResultCodeEnum.UNKNOWN_ERROR.getSuccess());
r.setCode(ResultCodeEnum.UNKNOWN_ERROR.getCode());
r.setMessage(ResultCodeEnum.UNKNOWN_ERROR.getMessage());
return r;
}
public static R setResult(ResultCodeEnum result) {
R r = new R();
r.setSuccess(result.getSuccess());
r.setCode(result.getCode());
r.setMessage(result.getMessage());
return r;
}
// Chainable methods
public R data(Map<String,Object> map) { this.setData(map); return this; }
public R data(String key,Object value) { this.data.put(key,value); return this; }
public R message(String message) { this.setMessage(message); return this; }
public R code(Integer code) { this.setCode(code); return this; }
public R success(Boolean success) { this.setSuccess(success); return this; }
}Controller Layer Return
@RestController
@RequestMapping("/api/v1/users")
public class TeacherAdminController {
@Autowired
private UserService userService;
@GetMapping
public R list() {
List<Teacher> list = teacherService.list(null);
return R.ok().data("items", list).message("用户列表");
}
}Example JSON response:
{
"success": true,
"code": 20000,
"message": "查询用户列表",
"data": {
"items": [
{"id":"1","username":"admin","role":"ADMIN","deleted":false,"gmtCreate":"2019-12-26T15:32:29","gmtModified":"2019-12-26T15:41:40"},
{"id":"2","username":"zhangsan","role":"USER","deleted":false,"gmtCreate":"2019-12-26T15:32:29","gmtModified":"2019-12-26T15:41:40"}
]
}
}Unified Exception Handling
When runtime exceptions occur, they cannot be returned via the normal R object, so a global exception handler is needed.
The @ControllerAdvice annotation collects @ExceptionHandler methods and applies them to all controllers.
Custom Global Exception Class
@Data
public class CMSException extends RuntimeException {
private Integer code;
public CMSException(Integer code, String message) {
super(message);
this.code = code;
}
public CMSException(ResultCodeEnum resultCodeEnum) {
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
}
@Override
public String toString() {
return "CMSException{" + "code=" + code + ", message=" + getMessage() + '}';
}
}Unified Exception Handler
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public R error(Exception e) {
e.printStackTrace();
return R.error();
}
@ExceptionHandler(NullPointerException.class)
@ResponseBody
public R error(NullPointerException e) {
e.printStackTrace();
return R.setResult(ResultCodeEnum.NULL_POINT);
}
@ExceptionHandler(HttpClientErrorException.class)
@ResponseBody
public R error(HttpClientErrorException e) {
e.printStackTrace();
return R.setResult(ResultCodeEnum.HTTP_CLIENT_ERROR);
}
@ExceptionHandler(CMSException.class)
@ResponseBody
public R error(CMSException e) {
e.printStackTrace();
return R.error().message(e.getMessage()).code(e.getCode());
}
}Controller Layer Display Example
{
"success": false,
"code": 20007,
"message": "空指针异常",
"data": {}
}Unified Log Collection
Logs are essential for tracing errors, especially in production environments.
Spring Boot integrates Logback, so Logback is recommended.
Logback Configuration (excerpt)
<configuration scan="true" scanPeriod="10 seconds">
<property name="log.path" value="D:/Documents/logs/edu"/>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- DEBUG_FILE, INFO_FILE, WARN_FILE, ERROR_FILE definitions omitted for brevity -->
<springProfile name="dev">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<springProfile name="pro">
<root level="info">
<appender-ref ref="ERROR_FILE"/>
<appender-ref ref="WARN_FILE"/>
</root>
</springProfile>
</configuration>Logging Exception Information
@Slf4j
public class ExceptionUtil {
/** Print exception stack trace as a string */
public static String getMessage(Exception e) {
try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
e.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
} catch (IOException ex) {
ex.printStackTrace();
log.error(ex.getMessage());
return null;
}
}
} @ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseBody
public R error(Exception e) {
log.error(ExceptionUtil.getMessage(e));
return R.error();
}
// other handlers omitted for brevity
}Note: The active Spring profile (spring.profiles.active) determines which logging configuration is used; logs are written to the directory defined by ${log.path}.
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 Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
