我們現在使用SpringBoot 做Web 開發已經比之前SprngMvc 那一套強大很多了。
但是 用SpringBoot Web 做API 開發還是不夠簡潔有一些。
每次Web API常用功能都需要重新寫一遍。或者複製之前專案程式碼。於是我封裝了這麼一個
抽出SpringBoot Web API 每個專案必備需要重複寫的模組,和必備功能。
並且擴充套件了我工作中用到的 所有工具庫。
基於它,你可以輕鬆開發SpringBoot WEB API,提高效率。不在去關心一些繁瑣。重複工作,而是把重點聚焦到業務。
目前更新版本到1.5.2 功能如下
後續會持續更新。專案中重複使用,必備模組和工具。
rest-api-spring-boot-starter 適用於SpringBoot Web API 快速構建讓開發人員快速構建統一規範的業務RestFull API 不在去關心一些繁瑣。重複工作,而是把重點聚焦到業務。
<dependency>
<groupId>cn.soboys</groupId>
<artifactId>rest-api-spring-boot-starter</artifactId>
<version>1.5.0</version>
</dependency>
@SpringBootApplication
@EnableRestFullApi
public class SuperaideApplication {
public static void main(String[] args) {
SpringApplication.run(SuperaideApplication.class, args);
}
}
到此你專案中就可以使用所有的功能了。
在Controller
中我們寫普通的請求介面如:
@PostMapping("/chat")
public HashMap chatDialogue() {
HashMap m = new HashMap();
m.put("age", 26);
m.put("name", "Judy");
return m;
}
返回的就是全域性統一RestFull API
{
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "IPbHLE5SZ1fqI0lgNXlB",
"timestamp": "2023-07-09 02:39:40",
"data": {
"name": "judy",
"hobby": "swing",
"age": 18
}
}
也可以基於Result
構建
@PostMapping("/chat")
public Result chatDialogue(@Validated EntityParam s) {
return Result.buildSuccess(s);
}
我們在日常中分頁是一個比較特殊返回。也是非常常用的。
@PostMapping("/page")
@Log("分頁查詢使用者資料")
public Result page(@Validated EntityParam s) {
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
List a=new ArrayList();
a.add(s);
resultPage.setPageData(a);
return ResultPage.buildSuccess(resultPage);
}
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
ResultPage.buildSuccess(resultPage)
進行構建返回返回統一響應格式
{
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "D9AMALgkZ6gVfe6Pi0Oh",
"timestamp": "2023-07-09 02:39:40",
"data": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
{
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "D9AMALgkZ6gVfe6Pi0Oh",
"timestamp": "2023-07-09 02:39:40",
"data": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
上述統一返回格式,可能不符合你專案中介面統一格式如:
{
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "ztf4S-lP9yrtKPSiwldZ",
"timestamp": "2023-07-11 13:46:53",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
}
page
分頁資料是在data
裡面你可以定義pageWrap
屬性true
包裝返回定義pageData
的key
值如records
等
你需要自定義key
如 msg
你可能對應message
,success
你可能對應status
只需要在組態檔中設定自定義key
自定義返回成功值你的成功返回可能是200
你可以設定code-success-value
值
rest-api:
enabled: false
msg: msg
code: code
code-success-value: OK
success: success
previousPage: previousPage
nextPage: nextPage
pageSize: pageSize
hasNext: hasNext
totalPageSize: totalPageSize
data: info
當 enabled
開啟後會讀取你自定義設定的key
如
rest-api:
enabled: true
msg: msg1
code: code1
code-success-value: 200
success: success1
previousPage: previousPage1
nextPage: nextPage1
pageSize: pageSize1
hasNext: hasNext1
totalPageSize: totalPageSize1
data: info
對應返回內容
{
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "ztf4S-lP9yrtKPSiwldZ",
"timestamp": "2023-07-11 13:46:53",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
}
有時候我們需要自定義返回。不去包裝統一響應RestFull API
格式
@NoRestFulApi
實現如@GetMapping("/test")
@NoRestFulApi
public Map chatDialogue() {
Map m= new HashMap<>();
m.put("name","judy");
m.put("age",26);
return m;
}
String
型別認為是頁面路徑。通過屬性組態檔include-packages
需要統一返回包。exclude-packages
不需統一返回的包
include-packages: cn.soboys.superaide.controller
exclude-packages: xx.xxx.xxx
已經內建自動支援。swagger
檔案。和最新的OpenApi3
檔案。專案啟動後即可存取。
swagger-ui.html 檔案。路徑/swagger-ui.html
基於spring-doc 檔案UI增強 路徑/doc.html
介面檔案屬性資訊
openapi:
description:
title:
version:
license:
contact:
name:
email:
url:
如果嫌棄官方提供的 swagger-ui 不美觀,或者使用不順手,可以選擇關閉 ui,還可以剔除掉 ui 相關的 webjar 的引入。
springdoc:
swagger-ui:
enabled: false
OpenAPI 檔案資訊,預設可在此 url 中獲取: http://server:port/context-path/v3/api-docs。
可以利用其他支援 OpenAPI 協定的工具,通過此地址,進行 API 展示,如 Apifox。
( Postman 的 api 測試也可以利用此地址進行匯入生成 )
Knife4j (原 swagger-bootstrap-ui) 3.x 版本提供了對於 OpenAPI 協定的部分支援。
::: tip
Knife4j 很多地方沒有按照協定規範實現,所以使用起來會有很多問題,另外專案也很久沒有維護了,不推薦使用。
:::
由於 knife4j 對於規範支援的不全面,無法直接使用單檔案源資料,所以必須進行分組或者 urls 的指定。
# urls
springdoc:
swagger-ui:
urls:
- { name: 'sample', url: '/v3/api-docs' }
或者
#分組
springdoc:
group-configs:
- { group: 'sample', packages-to-scan: 'com.example' }
Knife4j 的 UI 存取地址有所不同,頁面對映在 doc.html
路徑下,啟動專案後,存取 http://server:port/context-path/doc.html
即可進入 Knife4j 的 Swagger UI 頁面。
幫你封裝好了所有http常見錯誤,和所有請求引數驗證錯誤。
如請求錯誤
{
"success": false,
"code": "405",
"msg": "方法不被允許",
"timestamp": "2023-07-03 22:36:47",
"data": "Request method 'GET' not supported"
}
請求資源不存在等
{
"success": false,
"code": "404",
"msg": "請求資源不存在",
"timestamp": "2023-07-03 22:42:35",
"data": "/api"
}
如果需要攔截上面錯誤請在springboot 組態檔中加入
#出現錯誤時, 直接丟擲異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要為我們工程中的資原始檔建立對映
spring.web.resources.add-mappings=false
引數校驗錯誤
驗證Studen物件引數
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/6/26 22:10
* @webSite https://github.com/coder-amiao
*/
@Data
public class Student {
@NotBlank
private String nam;
@NotBlank
private String hobby;
}
@PostMapping("/chat")
public HashMap chatDialogue(@Validated Student student) {
HashMap m = new HashMap();
m.put("age", 26);
m.put("name", "Judy");
return m;
}
請求結果
JSON Body引數
@PostMapping("/chat")
public HashMap chatDialogue(@RequestBody @Validated Student student) {
HashMap m = new HashMap();
m.put("age", 26);
m.put("name", "Judy");
return m;
}
內建封裝錯誤預設支援英文和中文兩種國際化。你不做任何設定自動支援
如果需要內建支援更多語言,覆蓋即可。
自定義自己錯誤國際化和語言
i18n:
# 若前端無header傳參則返回中文資訊
i18n-header: Lang
default-lang: cn
message:
# admin
internal_server_error:
en: Internal Server Error
cn: 系統錯誤
not_found:
en: Not Found
cn: 請求資源不存在
message 對應錯誤提示
對應internal_server_error 自定義
下面語言自己定義 和前端傳入i18n-header 對應上,就顯你定義錯誤語言
我不傳錯誤國際化預設就是中文在 default-lang: cn
進行設定
當我傳入 指定語言 就會按照你設定的國際化自定義返回錯誤提示
RestFull API 統一返回有一個requestId
它是每個介面唯一標識。用於介面請求紀錄檔鏈路追蹤。紀錄檔查詢。 如:
{
"msg": "操作成功",
"code": "OK",
"previousPage": 1,
"success": true,
"requestId": "udYNdbbMFE45R84OPu9m",
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"timestamp": "2023-07-09 03:00:27",
"info": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
通過requestId你可以很輕鬆的在你的紀錄檔檔案查詢定位到每次錯誤的請求。
通過Log
註解記錄你想要記錄請求
@PostMapping("/page")
@Log(value = "查詢使用者資料",apiType= LogApiTypeEnum.USER,CURDType= LogCURDTypeEnum.RETRIEVE)
public Result page(@Validated EntityParam s) {
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
List a=new ArrayList();
a.add(s);
resultPage.setPageData(a);
return ResultPage.buildSuccess(resultPage);
}
系統預設紀錄檔記錄資料來源為紀錄檔檔案。如
2023-07-13 11:21:25 INFO http-nio-8888-exec-2 cn.soboys.restapispringbootstarter.aop.LimitAspect IP:192.168.1.8 第 1 次存取key為 [_kenx:chat192.168.1.8],描述為 [介面限流] 的介面
2023-07-13 11:21:26 INFO http-nio-8888-exec-2 cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource {
"description": "紀錄檔記錄測試",
"method": "cn.soboys.restapispringbootstarter.controller.ApiRestController.chatDialogue()",
"params": {
},
"logType": "INFO",
"requestIp": "192.168.1.8",
"path": "/chat",
"address": "0|0|0|內網IP|內網IP",
"time": 128,
"os": "Mac",
"browser": "Chrome",
"result": {
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "5RgKzWGFNa9XSPwhw2Pi",
"timestamp": "2023-07-13 11:21:25",
"data": "介面限流測試"
},
"apiType": "USER",
"device": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
}
你可以自定義自己的紀錄檔資料來源實現LogDataSource
介面 紀錄檔操作支援非同步。需要在設定類。或者啟動類加上@EnableAsync
註解
package cn.soboys.restapispringbootstarter.log;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* @Author: kenx
* @Since: 2021/6/23 13:55
* @Description:
*/
public interface LogDataSource {
/**
* 獲取拓展資料
* @return
* @param logEntry
*/
@Async
void save(LogEntry logEntry);
}
或者你可以繼承我預設的紀錄檔資料來源實現類LogFileDefaultDataSource
重寫save(LogEntry logEntry)
方法。
@Slf4j
public class LogFileDefaultDataSource implements LogDataSource {
/**
* 自定義儲存資料來源
*
* @param
* @return LogEntry
*/
@Override
public void save(LogEntry logEntry) {
log.info(JSONUtil.toJsonPrettyStr(logEntry));
}
}
如果是自定義紀錄檔資料來源實現需要再組態檔,設定紀錄檔資料來源。如:
logging:
path: ./logs #紀錄檔儲存路徑(伺服器上絕對)
max-history: 90 # 儲存多少天
max-file-size: 3MB # 每個檔案大小
max-total-size-cap: 1GB #總檔案大小超過多少壓縮
level-root: INFO # 這裡的INFO可以替換為其他紀錄檔等級,如DEBUG, WARN, ERROR, TRACE, FATAL, OFF等。 紀錄檔等級由低到高分別是debugger-info-warn-error
logDataSourceClass: cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource # 紀錄檔資料來源
紀錄檔記錄提供Ip
城市回顯記錄
@PostMapping("/page")
@Log(value = "查詢使用者資料", apiType = LogApiTypeEnum.USER, CURDType = LogCURDTypeEnum.RETRIEVE,ipCity = true)
public Result page(@Validated EntityParam s) {
ResultPage<List<EntityParam>> resultPage = new ResultPage<>();
List a = new ArrayList();
a.add(s);
resultPage.setPageData(a);
return ResultPage.buildSuccess(resultPage);
}
通過設定ipCity
屬性,預設是true
會記錄IP對應實體地址詳細資訊即國家城市等
Ip
城市查詢通過ip2region
獲取。
你可設定屬性 location
設定自己的ip2region.xdb
檔案。
ip2region:
external: false
location: classpath:ip2region/ip2region.xdb
預設不設定會幫你自動生成一個。基於2.7.x最新的資料
獲取最新自定義ip資料 Github 倉庫
當然也幫你封裝了。你可以通過工具類HttpUserAgent
的靜態方法getIpToCityInfo(String ip)
去獲取查詢ip對應城市資訊
設定語言國際化,紀錄檔等,
::: tip
預設不用設定任何引數。會使用預設的,設定了會使用你專案中的設定。
:::
rest-api:
enabled: false
msg: msg
code: code
code-success-value: OK
success: success
previousPage: previousPage
nextPage: nextPage
pageSize: pageSize
hasNext: hasNext
totalPageSize: totalPageSize
data: info
include-packages: cn.soboys.superaide.controller
exclude-packages: xx.xxx.xxx
redis:
key-prefix: rest
openapi:
description:
title:
version:
license:
contact:
name:
email:
url:
logging:
path: ./logs #紀錄檔儲存路徑(伺服器上絕對)
max-history: 90 # 儲存多少天
max-file-size: 3MB # 每個檔案大小
max-total-size-cap: 1GB #總檔案大小超過多少壓縮
level-root: INFO # 這裡的INFO可以替換為其他紀錄檔等級,如DEBUG, WARN, ERROR, TRACE, FATAL, OFF等。 紀錄檔等級由低到高分別是debugger-info-warn-error
logDataSourceClass: cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource # 紀錄檔資料來源
i18n:
# 若前端無header傳參則返回中文資訊
i18n-header: Lang
default-lang: cn
message:
# admin
internal_server_error:
en: Internal Server Error
cn: 系統錯誤
bad_gateway:
en: Bad Gateway
cn: 錯誤的請求
unauthorized:
en: Unauthorized
cn: 未授權
forbidden:
en: Forbidden
cn: 資源禁止存取
method_not_allowed:
en: Method Not Allowed
cn: 方法不被允許
request_timeout:
en: Request Timeout
cn: 請求超時
invalid_argument:
en: Invalid Argument {}
cn: 引數錯誤 {}
argument_analyze:
en: Argument Analyze {}
cn: 引數解析異常 {}
business_exception:
en: Business Exception
cn: 業務錯誤
not_found:
en: Not Found
cn: 請求資源不存在
支援MybatisPlus
程式碼一鍵生成 預設不引入MybatisPlus
生成依賴需要手動引入
package cn.soboys.restapispringbootstarter.config;
import lombok.Data;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/7/5 00:05
* @webSite https://github.com/coder-amiao
*/
@Data
public class GenerateCodeConfig {
/**
* 資料庫驅動
*/
private String driverName;
/**
* 資料庫連線使用者名稱
*/
private String username;
/**
* 資料庫連線密碼
*/
private String password;
/**
* 資料庫連線url
*/
private String url;
/**
* 生成程式碼 儲存路徑。預設當前專案下。
* 如需修改,使用覺得路徑
*/
private String projectPath;
/**
* 程式碼生成包位置
*/
private String packages;
}
@PostMapping("/chat")
public HashMap chatDialogue() {
HashMap m = new HashMap();
m.put("age", 26);
m.put("name", "Judy");
return m;
}
@PostMapping("/chat")
public Result chatDialogue() {
HashMap m = new HashMap();
m.put("age", 26);
m.put("name", "Judy");
return Result.buildSuccess(m);
}
我們在日常中分頁是一個比較特殊返回。也是非常常用的。
@PostMapping("/page")
@Log("分頁查詢使用者資料")
public Result page(@Validated EntityParam s) {
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
List a=new ArrayList();
a.add(s);
resultPage.setPageData(a);
return ResultPage.buildSuccess(resultPage);
}
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
ResultPage.buildSuccess(resultPage)
進行構建返回返回統一響應格式
{
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "D9AMALgkZ6gVfe6Pi0Oh",
"timestamp": "2023-07-09 02:39:40",
"data": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
{
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "D9AMALgkZ6gVfe6Pi0Oh",
"timestamp": "2023-07-09 02:39:40",
"data": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
上述統一返回格式,可能不符合你專案中介面統一格式如:
{
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "ztf4S-lP9yrtKPSiwldZ",
"timestamp": "2023-07-11 13:46:53",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
}
page
分頁資料是在data
裡面你可以定義pageWrap
屬性true
包裝返回定義pageData
的key
值如records
等
你需要自定義key
如 msg
你可能對應message
,success
你可能對應status
只需要在組態檔中設定自定義key
自定義返回成功值你的成功返回可能是200
你可以設定code-success-value
值
rest-api:
enabled: false
msg: msg
code: code
code-success-value: OK
success: success
previousPage: previousPage
nextPage: nextPage
pageSize: pageSize
hasNext: hasNext
totalPageSize: totalPageSize
data: info
當 enabled
開啟後會讀取你自定義設定的key
如
rest-api:
enabled: true
msg: msg1
code: code1
code-success-value: 200
success: success1
previousPage: previousPage1
nextPage: nextPage1
pageSize: pageSize1
hasNext: hasNext1
totalPageSize: totalPageSize1
data: info
對應返回內容
{
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "ztf4S-lP9yrtKPSiwldZ",
"timestamp": "2023-07-11 13:46:53",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
}
有時候我們需要自定義返回。不去包裝統一響應RestFull API
格式
@NoRestFulApi
實現如@GetMapping("/test")
@NoRestFulApi
public Map chatDialogue() {
Map m= new HashMap<>();
m.put("name","judy");
m.put("age",26);
return m;
}
String
型別認為是頁面路徑。通過屬性組態檔include-packages
需要統一返回包。exclude-packages
不需統一返回的包
include-packages: cn.soboys.superaide.controller
exclude-packages: xx.xxx.xxx
內建常見的錯誤。可以看HttpStatus。預設錯誤支援中文和英文兩種國際化。設定如下
i18n:
# 若前端無header傳參則返回中文資訊
i18n-header: Lang
default-lang: cn
message:
# admin
internal_server_error:
en: Internal Server Error
cn: 系統錯誤
bad_gateway:
en: Bad Gateway
cn: 錯誤的請求
unauthorized:
en: Unauthorized
cn: 未授權
forbidden:
en: Forbidden
cn: 資源禁止存取
method_not_allowed:
en: Method Not Allowed
cn: 方法不被允許
request_timeout:
en: Request Timeout
cn: 請求超時
invalid_argument:
en: Invalid Argument {}
cn: 引數錯誤 {}
argument_analyze:
en: Argument Analyze {}
cn: 引數解析異常 {}
business_exception:
en: Business Exception
cn: 業務錯誤
not_found:
en: Not Found
cn: 請求資源不存在
可以自行覆蓋擴充
預設攔所有未知錯誤異常和validation
引數校驗失敗異常,以及Http請求異常。
還有全域性自定義BusinessException 業務異常 自動整合spring-boot-starter-validation
你專案中不需要再單獨引入
<!--引數校驗-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
也內建擴充套件了許多自定義引數校驗參考
有時候我們專案中需要呼叫第三方介面服務。基於RestTemplate
進一步封裝了直接的POST,GET,請求。
在需要使用地方注入RestFulTemp
@Resource
private RestFulTemp restFulTemp;
@GetMapping("/doGet")
public Result doGet() {
ResponseEntity<String> response = restFulTemp.doGet("http://127.0.0.1:9000/redis/get");
return Result.buildSuccess();
}
/**
* POST 請求參 數為body json體格式
* @return
*/
@PostMapping("/doPost")
public Result doPost() {
Student s=new Student();
s.setHobby("swing");
s.setNam("judy");
//自動把物件轉換為JSON
ResponseEntity<String> response =
restFulTemp.doPost("http://127.0.0.1:9000/redis/get",s);
return Result.buildSuccess();
}
/**
* POST請求 引數為FORM 表單引數
* @return
*/
@PostMapping("/doPost")
public Result doPostForm() {
EntityParam s=new EntityParam();
s.setAge(19);
s.setHobby("swing");
s.setName("judy");
ResponseEntity<String> response =
restFulTemp.doPostForm("http://127.0.0.1:8000/chat", BeanUtil.beanToMap(s));
return Result.buildSuccess(response.getBody());
}
@GetMapping("/doDelete")
public Result doDelete() {
restFulTemp.doDelete("http://127.0.0.1:8000/chat");
return Result.buildSuccess();
}
@GetMapping("/doPut")
public Result doPut() {
EntityParam s=new EntityParam();
restFulTemp.doPut("http://127.0.0.1:8000/chat",s);
return Result.buildSuccess(s);
}
我內建錯誤異常和業務異常可能無法滿足你自身介面業務異常需要。你可以自定義錯誤異常類,和錯誤響應列舉碼。
自定義錯誤列舉 需要實現ResultCode介面
package cn.soboys.restapispringbootstarter;
import cn.soboys.restapispringbootstarter.i18n.I18NKey;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/6/26 10:21
* @webSite https://github.com/coder-amiao
* 響應碼介面,自定義響應碼,實現此介面
*/
public interface ResultCode extends I18NKey {
String getCode();
String getMessage();
}
如果要支援國際化還需要實現國際化介面I18NKey 參考我內部HttpStatus實現即可
package cn.soboys.restapispringbootstarter;
import cn.soboys.restapispringbootstarter.i18n.I18NKey;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/6/26 11:01
* @webSite https://github.com/coder-amiao
*/
public enum HttpStatus implements ResultCode, I18NKey {
/**
* 系統內部錯誤
*/
INTERNAL_SERVER_ERROR("500", "internal_server_error"),
BAD_GATEWAY("502", "bad_gateway"),
NOT_FOUND("404", "not_found"),
UNAUTHORIZED("401", "unauthorized"),
FORBIDDEN("403", "forbidden"),
METHOD_NOT_ALLOWED("405", "method_not_allowed"),
REQUEST_TIMEOUT("408", "request_timeout"),
INVALID_ARGUMENT("10000", "invalid_argument"),
ARGUMENT_ANALYZE("10001", "argument_analyze"),
BUSINESS_EXCEPTION("20000", "business_exception");
private final String value;
private final String message;
HttpStatus(String value, String message) {
this.value = value;
this.message = message;
}
@Override
public String getCode() {
return value;
}
@Override
public String getMessage() {
return message;
}
@Override
public String key() {
return message;
}
}
rest-api:
enabled: false
i18n:
# 若前端無header傳參則返回中文資訊
i18n-header: Lang
default-lang: cn
message:
# admin
internal_server_error:
en: Internal Server Error
cn: 系統錯誤
bad_gateway:
en: Bad Gateway
cn: 錯誤的請求
unauthorized:
en: Unauthorized
cn: 未授權
forbidden:
en: Forbidden
cn: 資源禁止存取
method_not_allowed:
en: Method Not Allowed
cn: 方法不被允許
request_timeout:
en: Request Timeout
cn: 請求超時
invalid_argument:
en: Invalid Argument {}
cn: 引數錯誤 {}
argument_analyze:
en: Argument Analyze {}
cn: 引數解析異常 {}
business_exception:
en: Business Exception
cn: 業務錯誤
not_found:
en: Not Found
cn: 請求資源不存在
封裝了業務錯誤斷言工具。Assert
遵循錯誤優先返回原則。
你要自定義自己的業務異常。繼承BusinessException
重寫對應方法
package cn.soboys.restapispringbootstarter.exception;
import cn.soboys.restapispringbootstarter.HttpStatus;
import cn.soboys.restapispringbootstarter.ResultCode;
import lombok.Data;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/6/26 16:45
* @webSite https://github.com/coder-amiao
*/
@Data
public class BusinessException extends RuntimeException {
/**
* 錯誤碼
*/
private String code="20000";
/**
* 錯誤提示
*/
private String message;
public BusinessException(String message) {
this.message = message;
}
public BusinessException(String message, String code) {
this.message = message;
this.code = code;
}
public BusinessException(ResultCode resultCode) {
this.message = resultCode.getMessage();
this.code = resultCode.getCode();
}
}
專案中紀錄檔是非常常用的,而且還是必須的。已經自動設定整合spring-boot-starter-logging
你不需要在專案中單獨引入
<!--紀錄檔整合-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
logging:
path: ./logs #紀錄檔儲存路徑(伺服器上絕對)
max-history: 90 # 儲存多少天
max-file-size: 3MB # 每個檔案大小
max-total-size-cap: 1GB #總檔案大小超過多少壓縮
level-root: INFO # 這裡的INFO可以替換為其他紀錄檔等級,如DEBUG, WARN, ERROR, TRACE, FATAL, OFF等。 紀錄檔等級由低到高分別是debugger-info-warn-error
logDataSourceClass: cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource # 紀錄檔資料來源
RestFull API 統一返回有一個requestId
它是每個介面唯一標識。用於介面請求紀錄檔鏈路追蹤。紀錄檔查詢。 如:
{
"msg": "操作成功",
"code": "OK",
"previousPage": 1,
"success": true,
"requestId": "udYNdbbMFE45R84OPu9m",
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"timestamp": "2023-07-09 03:00:27",
"info": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
]
}
通過requestId你可以很輕鬆的在你的紀錄檔檔案查詢定位到每次錯誤的請求。
通過Log
註解記錄你想要記錄請求
@PostMapping("/page")
@Log(value = "查詢使用者資料",apiType= LogApiTypeEnum.USER,CURDType= LogCURDTypeEnum.RETRIEVE)
public Result page(@Validated EntityParam s) {
ResultPage<List<EntityParam>> resultPage=new ResultPage<>();
List a=new ArrayList();
a.add(s);
resultPage.setPageData(a);
return ResultPage.buildSuccess(resultPage);
}
系統預設紀錄檔記錄資料來源為紀錄檔檔案。如
2023-07-09 03:00:32 INFO http-nio-8000-exec-2 cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource {
"description": "查詢使用者資料",
"method": "cn.soboys.restapispringbootstarter.controller.ApiRestController.page()",
"logType": "INFO",
"time": 3,
"result": {
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "udYNdbbMFE45R84OPu9m",
"timestamp": "2023-07-09 03:00:27",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
],
"requestId": "qJTOejQmY-OOf7fagegB",
"timestamp": "2023-07-09 03:00:27"
}
},
"apiType": "USER"
}
2023-07-09 03:08:03 INFO http-nio-8000-exec-4 cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource {
"description": "查詢使用者資料",
"method": "cn.soboys.restapispringbootstarter.controller.ApiRestController.page()",
"logType": "INFO",
"time": 1,
"result": {
"success": true,
"code": "OK",
"msg": "操作成功",
"requestId": "kP3yPP-H7wI2x1ak6YFA",
"timestamp": "2023-07-09 03:00:27",
"data": {
"previousPage": 1,
"nextPage": 1,
"pageSize": 1,
"totalPageSize": 1,
"hasNext": "false",
"pageData": [
{
"name": "judy",
"hobby": "swing",
"age": 18
}
],
"requestId": "pGbbiEj8GQ1eTxQpF2Jr",
"timestamp": "2023-07-09 03:00:27"
}
},
"apiType": "USER"
}
你可以自定義自己的紀錄檔資料來源實現LogDataSource
介面 紀錄檔操作支援非同步。需要在設定類。或者啟動類加上@EnableAsync
註解
package cn.soboys.restapispringbootstarter.log;
import org.springframework.scheduling.annotation.Async;
import java.util.Map;
/**
* @Author: kenx
* @Since: 2021/6/23 13:55
* @Description:
*/
public interface LogDataSource {
/**
* 獲取拓展資料
* @return
* @param logEntry
*/
@Async
void save(LogEntry logEntry);
}
或者你可以繼承我預設的紀錄檔資料來源實現類LogFileDefaultDataSource
重寫save(LogEntry logEntry)
方法。
@Slf4j
public class LogFileDefaultDataSource implements LogDataSource {
/**
* 自定義儲存資料來源
*
* @param
* @return LogEntry
*/
@Override
public void save(LogEntry logEntry) {
log.info(JSONUtil.toJsonPrettyStr(logEntry));
}
}
如果是自定義紀錄檔資料來源實現需要再組態檔,設定紀錄檔資料來源。如:
logging:
path: ./logs #紀錄檔儲存路徑(伺服器上絕對)
max-history: 90 # 儲存多少天
max-file-size: 3MB # 每個檔案大小
max-total-size-cap: 1GB #總檔案大小超過多少壓縮
level-root: INFO # 這裡的INFO可以替換為其他紀錄檔等級,如DEBUG, WARN, ERROR, TRACE, FATAL, OFF等。 紀錄檔等級由低到高分別是debugger-info-warn-error
logDataSourceClass: cn.soboys.restapispringbootstarter.log.LogFileDefaultDataSource # 紀錄檔資料來源
專案中快取使用是非常常見的。用的最多的是基於Redis
快取。於是我封裝了對於Redis
Key和Value常用操作。
::: tip
預設不引入Redis依賴,如果要使用Redis需要自己單獨引入
:::
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
注入redisTempUtil
@Autowired
private RedisTempUtil redisTempUtil;
如示列
@Autowired
private RedisTempUtil redisTempUtil;
@GetMapping("/redis")
public Result chatDialogue( ) {
redisTempUtil.set("test","111");
redisTempUtil.get("test");
redisTempUtil.set("user","userObj",7200l);
redisTempUtil.getAllKey("xx"); //*表示式
redisTempUtil.clean();
redisTempUtil.deleteObject("test");
redisTempUtil.hasKey("");
return Result.buildSuccess();
}
上面我們是直接通過工具類redisTempUtil
直接自己定義key
然後去儲存,這種方式是不可取的如果key很多隨意定義就會很混亂
。我提供了統一快取key管理介面CacheTmp
參考實現CacheKey
基於列舉形式,把所有key集中管理
package cn.soboys.restapispringbootstarter.cache;
import lombok.Getter;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/7/2 11:04
* @webSite https://github.com/coder-amiao
* 快取列舉
*/
@Getter
public enum CacheKey implements CacheTmp {
// 密碼的重置碼
PWD_RESET_CODE("reset:code:", true),
;
private String key;
/**
* Key是否是Key字首, true時直接取key=key,如果false時key=key+suffix
*/
private boolean hasPrefix;
CacheKey(String key, boolean hasPrefix) {
this.key = key;
this.hasPrefix = hasPrefix;
}
@Override
public Boolean getHasPrefix() {
return this.hasPrefix;
}
@Override
public String getKey() {
return this.key;
}
}
使用
@GetMapping("/redis")
public Result chatDialogue() {
CacheKey.PWD_RESET_CODE.valueSetAndExpire("test", 60l, TimeUnit.SECONDS, "judy");
return Result.buildSuccess();
}
@GetMapping("/redis/get")
public Result redisGet() {
String a = CacheKey.PWD_RESET_CODE.valueGet("judy");
return Result.buildSuccess(a);
}
封裝了spring Cache
進一步使用 專案中在設定類或者啟動類通過註解@EnableCaching
開啟直接使用即可
@Cacheable(cacheNames = "testCache", keyGenerator = "keyGeneratorStrategy")
@GetMapping("/redis/springCache")
public Result springCache() {
String a = "test cache";
return Result.buildSuccess(a);
}
工具類使用springCacheUtil
支援提供不是基於註解的使用方式
@GetMapping("/redis/springCache")
public Result redisSpringCache() {
String a = "111344";
springCacheUtil.putCache("test","key","121e1");
return Result.buildSuccess(a);
}
::: tip
預設不引入Redis依賴,快取基於記憶體實現(你專案引入redis依賴後會自定切換資料來源為Redis快取)
:::
多個專案或者模組使用一個key可能會造成混亂,於是提供了一個全域性設定key。
redis:
key-prefix: rest
程式碼中新增一個 String 型別的 key:testKey
,其實際在 redis 中儲存的 key name 為 rest:testKey
全域性 key 字首的設定,並不影響對 key 的其他操作,例如獲取對應的 value 時,依然是傳入 testKey
,而不是 rest:testKey
String key = "testKey";
String value = redisTempUtil.get(key);
String value1 = CacheKey.PWD_RESET_CODE.valueGet(key);
已經內建自動支援。swagger
檔案。和最新的OpenApi3
檔案。專案啟動後即可存取。
swagger-ui.html 檔案。路徑/swagger-ui.html
基於spring-doc 檔案UI增強 路徑/doc.html
介面檔案屬性資訊
openapi:
description:
title:
version:
license:
contact:
name:
email:
url:
如果嫌棄官方提供的 swagger-ui 不美觀,或者使用不順手,可以選擇關閉 ui,還可以剔除掉 ui 相關的 webjar 的引入。
springdoc:
swagger-ui:
enabled: false
OpenAPI 檔案資訊,預設可在此 url 中獲取: http://server:port/context-path/v3/api-docs。
可以利用其他支援 OpenAPI 協定的工具,通過此地址,進行 API 展示,如 Apifox。
( Postman 的 api 測試也可以利用此地址進行匯入生成 )
Knife4j (原 swagger-bootstrap-ui) 3.x 版本提供了對於 OpenAPI 協定的部分支援。
::: tip
Knife4j 很多地方沒有按照協定規範實現,所以使用起來會有很多問題,另外專案也很久沒有維護了,不推薦使用。
:::
由於 knife4j 對於規範支援的不全面,無法直接使用單檔案源資料,所以必須進行分組或者 urls 的指定。
# urls
springdoc:
swagger-ui:
urls:
- { name: 'sample', url: '/v3/api-docs' }
或者
#分組
springdoc:
group-configs:
- { group: 'sample', packages-to-scan: 'com.example' }
Knife4j 的 UI 存取地址有所不同,頁面對映在 doc.html
路徑下,啟動專案後,存取 http://server:port/context-path/doc.html
即可進入 Knife4j 的 Swagger UI 頁面。
專案中我們使用mybatis
或者mybatisPlus
一些簡單的單表業務程式碼,增刪改成。我們可以一鍵生成。不需要重複寫。 我封裝了mybatisPlus
程式碼生成工具
::: tip
預設不引入mybatisPlus
程式碼生成依賴,如果要使用mybatisPlus
程式碼生成需自行單獨引入
:::
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!--程式碼生成依賴的模板引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
package cn.soboys.restapispringbootstarter.config;
import lombok.Data;
/**
* @author 公眾號 程式設計師三時
* @version 1.0
* @date 2023/7/5 00:05
* @webSite https://github.com/coder-amiao
*/
@Data
public class GenerateCodeConfig {
/**
* 資料庫驅動
*/
private String driverName;
/**
* 資料庫連線使用者名稱
*/
private String username;
/**
* 資料庫連線密碼
*/
private String password;
/**
* 資料庫連線url
*/
private String url;
/**
* 生成程式碼 儲存路徑。預設當前專案下。
* 如需修改,使用絕對路徑
*/
private String projectPath;
/**
* 程式碼生成包位置
*/
private String packages;
}
示列如:
public class Test {
public static void main(String[] args) {
GenerateCodeConfig config=new GenerateCodeConfig();
config.setDriverName("com.mysql.cj.jdbc.Driver");
config.setUsername("root");
config.setPassword("root");
config.setUrl("jdbc:mysql://127.0.0.1:3306/ry?useUnicode=true&useSSL=false&characterEncoding=utf8");
//config.setProjectPath("superaide");
config.setPackages("cn.soboys.superaide");
MyBatisPlusGenerator.generate(config);
}
}
在使用過程中儘量使用最新版本。我會持續更新更多的內容。 會第一時間釋出在我的公眾號
程式設計師三時。全網同名
可以關注 公眾號 程式設計師三時。用心分享持續輸出優質內容。希望可以給你帶來一點幫助