在SqlSugar的開發架構的後端,我們基于Web API的封裝了統一的傳回結果,使得WebAPI的接口傳回值更加簡潔,而在前端,我們也需要統一對傳回的結果進行解析,并擷取和Web API接口對應的資料進行展示即可,本篇随筆介紹在Vue3+TypeScript+Vite的項目中,使用基于TypeScript的基類繼承的方式,實作對後端接口資料的統一解析處理的封裝操作。
1、SqlSugar的開發架構後端Web API的封裝
前面介紹到,在SqlSugar的開發架構的後端,我們需要對Web API統一封裝傳回結果,如對于授權登入的接口,我們的接口定義如下所示。
/// <summary>
/// 登入授權處理
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[HttpPost]
[Route("authenticate")]
public async
其中的Web API的傳回結果定義如下所示。
/// <summary>
/// 授權結果對象
/// </summary>
public class AuthenticateResultDto
{
/// <summary>
/// 令牌資訊
/// </summary>
public string? AccessToken { get; set; }
/// <summary>
/// 失效秒數
/// </summary>
public int Expires { get; set; }
/// <summary>
/// 處理是否成功
/// </summary>
public bool Succes { get; set; }
/// <summary>
/// 錯誤資訊
/// </summary>
public string? Error { get; set; }
}
我們注意到 Authenticate 的Web API方法傳回的結果是一些簡單的業務資訊,一般我們傳回結果需要在進行統一的封裝處理,以便達到統一的外部處理需要。
關于接口資料格式的統一封裝,我們定義一個WrapResultFilter,以及需要一個不封裝的屬性辨別DontWrapResultAttribute,預設是統一封裝傳回的結果。
而對于結果的統一封裝,我們隻需要在Web API服務啟動的時候,加入相關的過濾器處理即可。
//控制器添加自定義過濾器
builder.Services.AddControllers(options=>
{
options.Filters.Add<WrapResultFilter>(); //統一結果封裝處理
options.Filters.Add<GlobalExceptionFilter>();//自定義異常處理
});
//所有控制器啟動身份驗證
builder.Services.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter());//所有MVC服務預設添加授權标簽
如下是Web API統一封裝後傳回的結果對象。

而對于清單記錄的傳回,同樣是進行了外圍的封裝。
/// <summary>
/// 擷取所有記錄
/// </summary>
[HttpGet]
[Route("all")]
[HttpGet]public virtual async
2、利用axios元件對後端API資料的通路和基類的統一封裝處理
利用axios元件對後端Web API的調用,基本上是前端開發的标準做法了。
一般來說,我們為了友善,會對原生的axios元件進行一定的封裝處理,以便支援更好的調用處理,一般場景的操作就是POST、GET、PUT、DELETE,以及對檔案流的上傳處理操作,axios的github位址是https://github.com/axios/axios,如果需要可以參考它來做封裝處理即可。
本篇随筆基于https://github.com/vbenjs/vue-vben-admin 項目上的axios元件封裝進行使用,它對于axios元件的封裝比項目https://github.com/xiaoxian521/vue-pure-admin 上的封裝更加豐富一些,是以采用它。
利用axios元件,一般是為了友善,采用Typescript對它進行一定的封裝,并利于統一對Request和Response的對象統一攔截處理,如Request請求接口調用的時候,根據目前使用者的token進行頭部資訊的注入,擷取到接口後,可以對結果内容進行拆解,獲得簡化後的結果。
例如對于正常的POST、GET、PUT、DELETE的處理,統一進行了調用,根據配置參數進行處理
(
config: AxiosRequestConfig,
options?: RequestOptions
): Promise<T> {
return this.request({ ...config, method: "GET" }, options);
}
post<T = any>(
config: AxiosRequestConfig,
options?: RequestOptions
): Promise<T> {
return this.request({ ...config, method: "POST" }, options);
}
put<T = any>(
config: AxiosRequestConfig,
options?: RequestOptions
): Promise<T> {
return this.request({ ...config, method: "PUT" }, options);
}
delete<T = any>(
config: AxiosRequestConfig,
options?: RequestOptions
): Promise<T> {
return this.request({ ...config, method: "DELETE" }, options);
}
如對于HTTP請求攔截,我們需要在配置資訊中加入token令牌資訊,如下代碼所示。
/**
* @description: 請求攔截器處理
*/
requestInterceptors: (config, options) => {
// 請求之前處理config
const tokenString = getToken();
// console.log(tokenString);
if (tokenString) {
const data = JSON.parse(tokenString) as AuthenticateDto;
const now = new Date().getTime();
const expired = parseInt(data.expires) - now <= 0;
// console.log(data, now, expired);
if (expired) {
// token過期重新整理
}
const token = data.accessToken;
if (
token &&
(config as Recordable)?.requestOptions?.withToken !== false
) {
// jwt token
(config as Recordable).headers.Authorization =
options.authenticationScheme
? `${options.authenticationScheme} ${token}`
: token;
}
}
return config;
},
這些我們進行一定的微調即可,大多數情況下,不需要進行太多的設定。
對于統一傳回的結果,我們為了友善,統一進行了處理。在前端定義好幾個資料類型,最後傳回結果result即可。
在以前寫過的關于前端Web API的處理文章中,有《循序漸進VUE+Element 前端應用開發(19)--- 後端查詢接口和Vue前端的整合》 、《在Bootstrap開發架構基礎上增加WebApi+Vue&Element的前端》,都是對相關業務類進行接口的抽象封裝,以便于重用伺服器的邏輯調用。
Vue&Element的前端的架構設計如下所示。
一般來說,我們頁面子產品可能會涉及到Store子產品,用來存儲對應的狀态資訊,也可能是直接通路API子產品,實作資料的調用并展示。在頁面開發過程中,多數情況下,不需要Store子產品進行互動,一般隻需要存儲對應頁面資料為全局資料狀态的情況下,才可能啟用Store子產品的處理。
通過WebProxy代理的處理,我們可以很容易在前端中實作跨域的處理,不同的路徑調用不同的域名位址API都可以,最終轉換為本地的API調用,這就是跨域的處理操作。
3、通路後端接口的ES6 基類的封裝處理
前端根據架構後端的接口進行前端JS端的類的封裝處理,引入了ES6類的概念實作業務基類接口的統一封裝,簡化代碼。
權限子產品我們涉及到的使用者管理、機構管理、角色管理、菜單管理、功能管理、記錄檔、登入日志等業務類,那麼這些類繼承BaseApi,就會具有相關的接口了,如下所示繼承關系。
按照這個思路,我們在BaseApi的ES6類裡面定義了對應Web API基類裡面的操作方法,如下所示。
這樣,我們在建立一個業務類的時候,如果沒有特殊的自定義接口,隻需要繼承基類BaseApi即可具有所有的正常基類方法了。
// 導入API基類對象,預設具有Get/GetAll/Create/Update/Delete/BatchDelete/SaveImport/Count等接口
import BaseApi from "./base-api";
// 業務類自定義接口實作, 通用的接口已經在BaseApi中定義
class Api extends BaseApi {
// 參考下面案例,增加自定義函數
// GET 方法例子
// 根據條件計算記錄數量
// async GetCount(params: object) {
// return await this.HttpGet<number>(this.baseurl + "count", params);
// }
// POST 方法例子
// 建立對象
// async Create(data: object) {
// return await this.HttpPost<boolean>(this.baseurl + `create`, data);
// }
// PUT 方法例子
// 更新對象
// async Update(data: object) {
// return await this.HttpPut<boolean>(this.baseurl + `update`, data);
// }
// DELETE 方法例子
// 删除指定ID的對象
// async Delete(id: number | string) {
// return await this.HttpDelete<boolean>(this.baseurl + `${id}`);
// }
}
// 構造客戶資訊 Api執行個體,并傳遞業務類接口位址
export default new
如果需要一些定制的方法,我們則根據注釋的提示和Web API的路徑聲明進行編寫即可,如下是一個自定義接口的處理。
// 根據字典類型擷取對應的TreeNodeItem集合(包括id, label屬性)
async GetTreeItemByDictType(dictTypeName: string) {
return await this.HttpGet<TreeNodeItem[]>(
this.baseurl + `treeitem-by-typename/${dictTypeName}`
);
}
由于是基于TypeScript,我們在具體的位置中定義了TreeNodeItem類型,對應伺服器傳回的WebAPI類型即可。
//樹節點類型
export interface TreeNodeItem {
id: string;
label: string;
key?: string;
children?: TreeNodeItem[];
}
然後在自定義的ES6類的頂部引入類型定義就可以了
import {
PagedResult,
ListResult,
TreeNodeItem,
CListItem,
CommonResult
} from "./types";
我們定義了接口後,就可以在Vue的JS裡面進行調用了。
// 使用字典類型,從伺服器請求資料
await dictdata.GetTreeItemByDictType(typeName).then(list => {
if (list) {
list.forEach(item => {
dictItems.value.push({ id: item.id, label: item.label });
});
}
});
我們也可以使用async/await的異步線程調用方法,如下所示。
另外,由于我們的ES6接口定義,是基于TypeScript的,它的資料類型可以推斷出來,是以在編碼或者檢視對應屬性的時候,會有非常好的提示資訊,如上所示。
最後我們來驗證下實際的axios調用頁面的效果。
以及另外一個複雜一點的測試頁面展示。