採用先後端分離的方式進行項目開發,那麼先後端交互比較好的方式是採用HTTP+JSON。如何接口返回結果更加簡潔,更加優雅,也更加合理,而且讓前端開發人員看得明白,後端開發者也並不會所以而增長工做量呢?前端
Hello,各位,很久不見了。一直在籌劃我的網站2020版本改版的事情,因此,本篇文章,也是2019年最後一篇了,固然,也得花一些心思,爭取把我想要說的話,都一一說出來,說明白,說透徹。java
採用先後端分離的方式進行項目開發,那麼先後端交互比較好的方式是採用HTTP+JSON。如何接口返回結果更加簡潔,更加優雅,也更加合理,而且讓前端開發人員看得明白,後端開發者也並不會所以而增長工做量呢?git
爲此,我寫了一套關於API接口開發返回結果解決方案, api-result,已將其開源,並上傳到中央倉庫,歡迎各位批評和指正。github
提供了知足各場景使用的實體類,以下:json
這個類是基礎實體類,有以下屬性:後端
success:返回結果標識,是一個布爾值,true / false(成功 / 失敗)api
message:描述信息,錯誤時,能夠在這裏填寫錯誤的詳細信息數組
data:數據,是一個泛型,能夠是數組或者對象等等,成功而且須要返回數據時,纔有該參數bash
結構關係以下:app
ResultModel
└── ApiResultModel
複製代碼
在這個類裏面增長了 code
屬性,也是一個泛型,你能夠自定義你的返回碼類型,能夠是整數,或者字符串。
結構關係以下:
ResultModel
└── PageResultModel
複製代碼
這個類主要主要分頁返回結果,因此,增長了如下屬性:
total:數據總條數,Long型
size:每頁條數,整數
pages:總頁數,Long型
current:當前頁,Long型
幫助咱們操做實體類,具體有哪些helper呢?以下:
ResultHelper是與ResultModel對應的
成功,攜帶描述信息
成功,攜帶描述信息和數據
錯誤,攜帶詳細的描述信息
ApiResultHelper是與ApiResultModel對應的
成功,攜帶返回碼和描述信息
成功,攜帶返回碼、描述信息和數據
錯誤,攜帶錯誤碼和詳細描述信息
PageResultHelper是與PageResultModel對應的
成功,攜帶描述信息
成功,攜帶描述信息和數據
成功,攜帶描述信息、數據、總數、每頁條數、總頁數、當前頁
錯誤,攜帶詳細的描述信息
咱們爲你提供了三個實體類,以知足不一樣場景,ResultModel適用於一般返回結果,ApiResultModel適用於接口開發返回結果,PageResultModel適用於分頁返回結果。也分別爲這三個實體類提供了各自的Helper,因此,你能夠直接使用這些Helper進行返回。固然,我推薦的使用方式是,先爲各Helper編寫工具類,再經過工具類進行返回,這樣可能更加合理定製本身的返回工具類。
首先咱們來看一個簡單的示例代碼:
/** * 添加方法示例 * @return {@link ResultModel} */
@ApiOperation(value = "添加方法示例")
@PostMapping
public ResultModel<?> add() {
return ResultHelper.success("添加成功");
}
複製代碼
返回結果:
{
"success": true,
"message": "添加成功"
}
複製代碼
注:這只是一個接口返回示例,而不是說添加接口應該這樣寫。
好比,咱們能夠寫一個ResultUtils工具類來操做ResultHelper。以下示例:
/** * 成功示例 * @return {@link ResultModel} */
public static ResultModel <?> success() {
return ResultHelper.success("Success");
}
複製代碼
我門就能夠調用ResultUtils類裏面的方法,以下示例:
/** * 成功示例 * @return {@link ResultModel} */
@ApiOperation(value = "成功示例")
@DeleteMapping
public ResultModel<?> success() {
return ResultUtils.success();
}
複製代碼
印象結果:
{
"success": true,
"message": "Success"
}
複製代碼
/** * 刪除方法示例 * @return {@link ResultModel} */
@ApiOperation(value = "刪除方法示例")
@DeleteMapping
public ResultModel<?> delete() {
return ResultUtils.success();
}
複製代碼
響應結果:
{
"success": true,
"message": "Success"
}
複製代碼
若是操做出錯了,咱們怎麼返回呢?咱們來看一下:
/** * 修改方法示例 * @return {@link ResultModel} */
@ApiOperation(value = "修改方法示例")
@PutMapping
public ResultModel<?> update() {
return ResultUtils.error("修改失敗");
}
複製代碼
返回結果:
{
"success": false,
"message": "修改失敗"
}
複製代碼
值得一提的話,就是查詢方法了,咱們看一下吧
/** * 查詢方法示例 * @return {@link ResultModel} */
@ApiOperation(value = "查詢方法示例")
@GetMapping
public ResultModel<?> get() {
List<Map<String, String>> list = new ArrayList<>();
Map<String, String> map1 = new HashMap<>();
map1.put("name", "張飛");
map1.put("desc", "燕人張飛");
list.add(map1);
Map<String, String> map2 = new HashMap<>();
map2.put("name", "趙雲");
map2.put("desc", "常山趙子龍");
list.add(map2);
Map<String, String> map3 = new HashMap<>();
map3.put("name", "關羽");
map3.put("desc", "溫酒斬華雄");
list.add(map3);
return ResultUtils.success(list);
}
複製代碼
看一下響應結果吧,是否如你所願:
{
"success": true,
"message": "Success",
"data": [
{
"name": "張飛",
"desc": "燕人張飛"
},
{
"name": "趙雲",
"desc": "常山趙子龍"
},
{
"name": "關羽",
"desc": "溫酒斬華雄"
}
]
}
複製代碼
/** * 接口返回數據示例 * @return {@link ApiResultModel} */
@ApiOperation(value = "接口返回數據示例")
@GetMapping("/api-data")
public ApiResultModel<Integer, ?> apiData() {
return ApiResultUtils.success(getData());
}
複製代碼
響應結果:
{
"success": true,
"message": "Success",
"data": [
{
"name": "張飛",
"desc": "燕人張飛"
},
{
"name": "趙雲",
"desc": "常山趙子龍"
},
{
"name": "關羽",
"desc": "溫酒斬華雄"
}
],
"code": 0
}
複製代碼
/** * API接口錯誤返回示例 * @return {@link ApiResultModel} */
@ApiOperation(value = "API接口錯誤返回示例")
@GetMapping("/api-error")
public ApiResultModel<Integer, ?> apiError() {
return ApiResultUtils.error(1101, "API接口錯誤返回示例");
}
複製代碼
響應結果:
{
"success": false,
"message": "API接口錯誤返回示例",
"code": 1101
}
複製代碼
/** * 分頁返回數據示例 * @return {@link ApiResultModel} */
@ApiOperation(value = "分頁返回數據示例")
@GetMapping("/page")
public PageResultModel<?> page() {
return PageResultUtils.success(getData(), 100, 10, 10, 1);
}
複製代碼
響應結果:
{
"success": true,
"message": "Success",
"data": [
{
"name": "張飛",
"desc": "燕人張飛"
},
{
"name": "趙雲",
"desc": "常山趙子龍"
},
{
"name": "關羽",
"desc": "溫酒斬華雄"
}
],
"total": 100,
"size": 10,
"pages": 10,
"current": 1
}
複製代碼
package com.fengwenyi.api_example.util;
import com.fengwenyi.api_result.helper.ResultHelper;
import com.fengwenyi.api_result.model.ResultModel;
/** * 返回結果封裝工具類 * @author Erwin Feng[xfsy_2015@163.com] * @since 2019/11/30 13:54 */
public class ResultUtils {
/** * 成功 * @return {@link ResultModel} */
public static ResultModel <?> success() {
return ResultHelper.success("Success");
}
/** * 成功,攜帶數據 * @param data 數據 * @param <T> 數據的類型 * @return {@link ResultModel} */
public static <T> ResultModel <T> success(T data) {
return ResultHelper.success("Success", data);
}
/** * 錯誤,攜帶詳細的錯誤描述信息 * @param message 詳細的錯誤描述信息 * @return {@link ResultModel} */
public static ResultModel <?> error(String message) {
return ResultHelper.error(message);
}
}
複製代碼
package com.fengwenyi.api_example.util;
import com.fengwenyi.api_result.helper.ApiResultHelper;
import com.fengwenyi.api_result.model.ApiResultModel;
/** * API接口返回結果工具類 * @author Erwin Feng[xfsy_2015@163.com] * @since 2019/12/1 20:10 */
public class ApiResultUtils {
/** * 成功,攜帶返回碼和描述信息 * @return {@link ApiResultModel} */
public static ApiResultModel<Integer, ?> success() {
return ApiResultHelper.success(0, "Success");
}
/** * 成功,攜帶返回碼、描述信息和數據 * @param data 數據 * @param <T> 數據的類型 * @return {@link ApiResultModel} */
public static <T> ApiResultModel<Integer, T> success(T data) {
return ApiResultHelper.success(0, "Success", data);
}
/** * 出錯,攜帶錯誤嗎和詳細描述信息 * @param code 返回碼 * @param message 相信描述信息 * @return {@link ApiResultModel} */
public static ApiResultModel<Integer, ?> error(int code, String message) {
return ApiResultHelper.error(code, message);
}
}
複製代碼
package com.fengwenyi.api_example.util;
import com.fengwenyi.api_result.helper.PageResultHelper;
import com.fengwenyi.api_result.model.PageResultModel;
/** * 分頁返回結果工具類 * @author Erwin Feng[xfsy_2015@163.com] * @since 2019/12/1 20:32 */
public class PageResultUtils {
/** * 成功,攜帶分頁相關數據以及信息 * @param data 數據 * @param total 數據總條數 * @param size 每頁條數 * @param pages 總頁數 * @param current 當前頁 * @param <T> 數據類型 * @return {@link PageResultModel} */
public static <T> PageResultModel<T> success(T data, long total, int size, long pages, long current) {
return PageResultHelper.success("Success", data, total, size, pages, current);
}
}
複製代碼
這裏補充一下,關於如何解析返回的json字符串,談談個人見解吧。返回的是一個json格式的字符串,這裏我用fastjson來寫解析示例。咱們一般會將請求數據封裝爲一個通用方法或者工具類,只須要返回數據,固然,若是失敗,或者出現異常,都在這裏處理。
/** * 解析經常使用返回結果示例 * @return 數據 */
public Object parseResult() {
String result = "";
ResultModel<?> resultModel = JSON.parseObject(result, ResultModel.class);
Boolean success = resultModel.getSuccess();
if (success != null && success) {
return resultModel.getData();
} else {
// 異常信息
String message = resultModel.getMessage();
// 異常處理
throw new DataParseException(message);
}
}
複製代碼
/** * 解析接口返回結果示例 * @return 數據 */
public Object parseApiResult() {
String apiResult = "";
ApiResultModel<?, ?> apiResultModel = JSON.parseObject(apiResult, ApiResultModel.class);
Boolean success = apiResultModel.getSuccess();
if (success != null && success) {
return apiResultModel.getData();
} else {
Object code = apiResultModel.getCode();
String message = apiResultModel.getMessage();
// 根據接口錯誤碼分別進行處理
// ...
return null;
}
}
複製代碼
這裏與上面略有不一樣,由於,增長了一些字段,因此,咱們能夠藉助bean來返回。
/** * 解析分頁返回結果示例 * @return {@link PageResultDataBean} */
public PageResultDataBean parsePageResult() {
String pageResult = "";
PageResultModel<List<?>> pageResultModel = JSON.parseObject(pageResult, PageResultModel.class);
Boolean success = pageResultModel.getSuccess();
if (success != null && success) {
List<?> data = pageResultModel.getData();
Long total = pageResultModel.getTotal();
Integer size = pageResultModel.getSize();
Long pages = pageResultModel.getPages();
Long current = pageResultModel.getCurrent();
return new PageResultDataBean()
.setTotal(total)
.setSize(size)
.setPages(pages)
.setCurrent(current)
.setData(data);
} else {
// 異常信息
String message = pageResultModel.getMessage();
// 異常處理
throw new DataParseException(message);
}
}
複製代碼
以上,這一切都是否如你所願呢?歡迎評論留言告訴我。
[3] api-result中央倉庫
[4] 測試示例代碼