????????在我們的項目開發(fā)中,我們都會對數據返回格式進行統(tǒng)一的處理,這樣可以方便前端人員取數據,當然除了正常流程的數據返回格式需要統(tǒng)一以外,我們也需要對異常的情況進行統(tǒng)一的處理,以及項目必備的日志。
1. 統(tǒng)一返回格式
????????在項目開發(fā)中返回的是json格式的數據,也就是統(tǒng)一json數據返回格式,一般情況下返回數據的基本格式包含是否成功、響應狀態(tài)碼、返回的消息、以及返回的數據。格式如下:
{
"success": 布爾, // 是否成功
"code": 數字, // 響應狀態(tài)碼
"message": 字符串, // 返回的消息
"data": {} // 放置響應的數據
}
1.1?添加枚舉類
????????該類定義了以上統(tǒng)一格式的前三部分:是否成功、響應狀態(tài)碼、返回的消息;可自行根據項目需要進行后續(xù)的添加或者刪改。
創(chuàng)建一個result包,下面放置ResultCodeEnum
枚舉類
/**
* 狀態(tài)碼
*
*/
public enum ResultCodeEnum {
SUCCESS(true, 20000, "成功"),
UNKNOWN_REASON(false, 20001, "未知錯誤");
private final Boolean success;
private final Integer code;
private final String message;
ResultCodeEnum(Boolean success, Integer code, String message) {
this.success = success;
this.code = code;
this.message = message;
}
public Boolean getSuccess() {
return success;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "ResultCodeEnum{" + "success=" + success + ", code=" + code + ", message='" + message + '\'' + '}';
}
}
1.2 添加統(tǒng)一返回格式的類
該類是用來和前端交互的類,定義的就是本文開頭所說的格式
在result包下創(chuàng)建一個統(tǒng)一返回格式的類R
/**
* 統(tǒng)一返回格式類
*
*/
public class R {
/**
* 是否成功
*/
private Boolean success;
/**
* 狀態(tài)碼
*/
private Integer code;
/**
* 返回的消息
*/
private String message;
/**
* 放置響應的數據
*/
private Map<String, Object> data = new HashMap<>();
public R() {}
/** 以下是定義一些常用到的格式,可以看到調用了我們創(chuàng)建的枚舉類 */
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_REASON.getSuccess());
r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());
r.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage());
return r;
}
public static R setResult(ResultCodeEnum resultCodeEnum) {
R r = new R();
r.setSuccess(resultCodeEnum.getSuccess());
r.setCode(resultCodeEnum.getCode());
r.setMessage(resultCodeEnum.getMessage());
return r;
}
public R success(Boolean success) {
this.setSuccess(success);
return this;
}
public R message(String message) {
this.setMessage(message);
return this;
}
public R code(Integer code) {
this.setCode(code);
return this;
}
public R data(String key, Object value) {
this.data.put(key, value);
return this;
}
public R data(Map<String, Object> map) {
this.setData(map);
return this;
}
/** 以下是get/set方法,如果項目有集成lombok可以使用@Data注解代替 */
public Boolean getSuccess() {
return success;
}
public void setSuccess(Boolean success) {
this.success = success;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<String, Object> getData() {
return data;
}
public void setData(Map<String, Object> data) {
this.data = data;
}
}
?1.3?測試
/**
* 測試控制器
*
*/
@RestController
@RequestMapping("testR")
public class TestController {
@GetMapping("ok")
public R testOk() {
Map<String, Object> data = new HashMap<>();
data.put("name", "李太白");
return R.ok().data(data);
}
}
????????可以看到格式是正確的,只要我們返回數據的時候使用R這個類返回就行了,不過有一種情況,就是當我們代碼中拋出異常之后返回的格式就不是這樣子了,下面我演示一下在代碼中添加int a = 1/0
的語句,肯定導致拋異常的;?
/**
* 測試控制器
*
*/
@RestController
@RequestMapping("testR")
public class TestController {
@GetMapping("ok")
public R testOk() {
int a = 1/0;
Map<String, Object> data = new HashMap<>();
data.put("name", "李太白");
return R.ok().data(data);
}
}
????????可以發(fā)現返回的格式已經不是我們所需要的格式了,這種情況會給前端人員帶來不必要的麻煩,所以我們也需要對異常情況進行統(tǒng)一的格式處理;?
2. 統(tǒng)一異常處理
????????經過上面的演示,相信你已經明白我們?yōu)槭裁葱枰M行統(tǒng)一的異常處理了,當然處理統(tǒng)一的異常處理以外我們在開發(fā)項目中也會主動的拋出異常,像這種情況我們需要配合自定義異常來完成;
2.1 添加統(tǒng)一異常處理器
創(chuàng)建一個handler包,在該包下面添加GlobalExceptionHandler類
/**
* 統(tǒng)一異常處理
* ControllerAdvice注解的含義是當異常拋到controller層時會攔截下來
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 使用ExceptionHandler注解聲明處理Exception異常
*
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public R exception(Exception e) {
// 控制臺打印異常
e.printStackTrace();
// 返回錯誤格式信息
return R.error();
}
}
2.2?測試統(tǒng)一異常處理
????????可以看到現在出現異常之后返回的格式已經是我們所需要的格式了,如果我們想讓這個錯誤信息更加明確,我們可以通過添加自定義異常來實現。
2.3?添加自定義異常類
新建exception包,在該包下添加自定義異常類
/**
* 測試自定義異常類
* 需要繼承運行時異常RuntimeException
*/
public class TestException extends RuntimeException {
private Integer code;
public TestException(ResultCodeEnum resultCodeEnum) {
// 調用父類的方法添加信息
super(resultCodeEnum.getMessage());
this.code = resultCodeEnum.getCode();
}
public Integer getCode() {
return code;
}
}
?2.4?在統(tǒng)一異常處理類GlobalExceptionHandler中添加一個自定義異常的處理
/**
* 統(tǒng)一異常處理
* ControllerAdvice注解的含義是當異常拋到controller層時會攔截下來
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 使用ExceptionHandler注解聲明處理Exception異常
*
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public R exception(Exception e) {
// 控制臺打印異常
e.printStackTrace();
// 返回錯誤格式信息
return R.error();
}
/**
* 使用ExceptionHandler注解聲明處理TestException異常
*
*/
@ResponseBody
@ExceptionHandler(TestException.class)
public R exception(TestException e) {
// 控制臺打印異常
e.printStackTrace();
// 返回錯誤格式信息
return R.error().message(e.getMessage()).code(e.getCode());
}
}
?2.5?測試自定義異常
在枚舉類中添加一個狀態(tài)信息
TEST_NUMBER(false, 500, "計算錯誤");
/**
* 測試控制器
*
*/
@RestController
@RequestMapping("testR")
public class TestController {
@GetMapping("ok")
public R testOk() {
try{
int a = 1/0;
}catch{
throw new TestException(ResultCodeEnum.TEST_NUMBER);
}
Map<String, Object> data = new HashMap<>();
data.put("name", "李太白");
return R.ok().data(data);
}
}
3. 統(tǒng)一日志處理?
為了更方便我們進行錯誤的調式,一般會在項目中集成日志。
3.1?添加日志配置文件
在resources下添加日志的配置,文件名必須是logback-spring.xml
以下配置一般不需要修改,要改的話也只是修改日志的輸出目錄
<property name="log.path" value="D:/javaWeb/log" />
value就是日志的輸出位置
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="10 seconds">
<!-- 日志級別從低到高分為TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果設置為WARN,則低于WARN的信息都不會輸出 -->
<!-- scan:當此屬性設置為true時,配置文件如果發(fā)生改變,將會被重新加載,默認值為true -->
<!-- scanPeriod:設置監(jiān)測配置文件是否有修改的時間間隔,如果沒有給出時間單位,默認單位是毫秒。當scan為true時,此屬性生效。默認的時間間隔為1分鐘。 -->
<!-- debug:當此屬性設置為true時,將打印出logback內部日志信息,實時查看logback運行狀態(tài)。默認值為false。 -->
<contextName>logback</contextName>
<!-- name的值是變量的名稱,value的值時變量定義的值。通過定義的值會被插入到logger上下文中。定義變量后,可以使“${}”來使用變量。 -->
<property name="log.path" value="D:/javaWeb/log" />
<!--控制臺日志格式:彩色日志-->
<!-- magenta:洋紅 -->
<!-- boldMagenta:粗紅-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋紅 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN"
value="%date{yyyy-MM-dd HH:mm:ss} |%-5level |%thread |%file:%line |%logger |%msg%n" />
<!--編碼-->
<property name="ENCODING"
value="UTF-8" />
<!--輸出到控制臺-->
<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>${ENCODING}</charset>
</encoder>
</appender>
<!--輸出到文件-->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--日志過濾器:此日志文件只記錄INFO級別的-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${log.path}/log_info.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
<!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志歸檔路徑以及格式 -->
<fileNamePattern>${log.path}/info/log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天數-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志過濾器:此日志文件只記錄WARN級別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>WARN</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${log.path}/log_warn.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此處設置字符集 -->
</encoder>
<!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/warn/log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天數-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志過濾器:此日志文件只記錄ERROR級別的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<!-- 正在記錄的日志文件的路徑及文件名 -->
<file>${log.path}/log_error.log</file>
<encoder>
<pattern>${FILE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset> <!-- 此處設置字符集 -->
</encoder>
<!-- 日志記錄器的滾動策略,按日期,按大小記錄 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/error/log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天數-->
<maxHistory>15</maxHistory>
</rollingPolicy>
</appender>
<!--開發(fā)環(huán)境-->
<springProfile name="dev">
<!--可以靈活設置此處,從而控制日志的輸出-->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
<!--生產環(huán)境-->
<springProfile name="pro">
<root level="ERROR">
<appender-ref ref="ERROR_FILE" />
</root>
</springProfile>
</configuration>
3.2?添加application.properties配置
配置文件需要設置下環(huán)境,需要跟日志配置文件中的<springProfile name="dev">
對應上,不然不生效
# 設置環(huán)境
spring.profiles.active=dev
3.3?修改GlobalExceptionHandler類
/**
* 統(tǒng)一異常處理
* ControllerAdvice注解的含義是當異常拋到controller層時會攔截下來
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 打印日志
* 如果項目有集成lombok可使用@Slf4j注解代替
*/
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 使用ExceptionHandler注解聲明處理Exception異常
*
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public R exception(Exception e) {
// 控制臺打印異常
log.error(e.getMessage());
// 返回錯誤格式信息
return R.error();
}
/**
* 使用ExceptionHandler注解聲明處理TestException異常
*
*/
@ResponseBody
@ExceptionHandler(TestException.class)
public R exception(TestException e) {
// 控制臺打印異常
log.error(e.getMessage());
// 返回錯誤格式信息
return R.error().message(e.getMessage()).code(e.getCode());
}
}
3.4?測試效果
日志生效了,而且在我們的D盤javaWeb目錄下也有對應的日志文件了
?我們可以進一步的完善下,將日志堆棧信息輸出到文件
3.5?定義工具類
?新建utils包,在該包下添加ExceptionUtils類
/**
* 日志堆棧信息輸出到文件工具類
*
*/
public class ExceptionUtils {
public static String getMessage(Exception e) {
StringWriter sw = null;
PrintWriter pw = null;
try {
sw = new StringWriter();
pw = new PrintWriter(sw);
// 將出錯的棧信息輸出到printWriter中
e.printStackTrace(pw);
pw.flush();
sw.flush();
} finally {
if (sw != null) {
try {
sw.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (pw != null) {
pw.close();
}
}
return sw.toString();
}
}
3.6?再修改GlobalExceptionHandler類
/**
* 統(tǒng)一異常處理
* ControllerAdvice注解的含義是當異常拋到controller層時會攔截下來
*/
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 打印日志 如果項目有集成lombok可使用@Slf4j注解代替
*/
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 使用ExceptionHandler注解聲明處理Exception異常
*
*/
@ResponseBody
@ExceptionHandler(Exception.class)
public R exception(Exception e) {
// 控制臺打印異常 借助工具類將錯誤堆棧輸出到文件
log.error(ExceptionUtils.getMessage(e));
// 返回錯誤格式信息
return R.error();
}
/**
* 使用ExceptionHandler注解聲明處理TestException異常
*
*/
@ResponseBody
@ExceptionHandler(TestException.class)
public R exception(TestException e) {
// 控制臺打印異常 借助工具類將錯誤堆棧輸出到文件
log.error(ExceptionUtils.getMessage(e));
// 返回錯誤格式信息
return R.error().message(e.getMessage()).code(e.getCode());
}
}
以上是根據一位博主的文章編寫的,現在找不到那篇文章了,還請見諒。
這篇文章就到這里了,下次見!
??原創(chuàng)不易,還希望各位大佬支持一下!
??點贊,你的認可是我創(chuàng)作的動力?!
??收藏,你的青睞是我努力的方向!文章來源:http://www.zghlxwxcb.cn/news/detail-804629.html
??評論,你的意見是我進步的財富!文章來源地址http://www.zghlxwxcb.cn/news/detail-804629.html
到了這里,關于Spring Boot 優(yōu)雅實現統(tǒng)一數據返回格式+統(tǒng)一異常處理+統(tǒng)一日志處理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!