天天看點

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

在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統一封裝後傳回的結果對象。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

而對于清單記錄的傳回,同樣是進行了外圍的封裝。

/// <summary>
        /// 擷取所有記錄
        /// </summary>
        [HttpGet]
        [Route("all")]
        [HttpGet]public virtual async      
基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

 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即可。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

在以前寫過的關于前端Web API的處理文章中,有《​​循序漸進VUE+Element 前端應用開發(19)--- 後端查詢接口和Vue前端的整合​​​》 、《​​在Bootstrap開發架構基礎上增加WebApi+Vue&Element的前端​​》,都是對相關業務類進行接口的抽象封裝,以便于重用伺服器的邏輯調用。

Vue&Element的前端的架構設計如下所示。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

一般來說,我們頁面子產品可能會涉及到Store子產品,用來存儲對應的狀态資訊,也可能是直接通路API子產品,實作資料的調用并展示。在頁面開發過程中,多數情況下,不需要Store子產品進行互動,一般隻需要存儲對應頁面資料為全局資料狀态的情況下,才可能啟用Store子產品的處理。

通過WebProxy代理的處理,我們可以很容易在前端中實作跨域的處理,不同的路徑調用不同的域名位址API都可以,最終轉換為本地的API調用,這就是跨域的處理操作。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

3、通路後端接口的ES6 基類的封裝處理

前端根據架構後端的接口進行前端JS端的類的封裝處理,引入了ES6類的概念實作業務基類接口的統一封裝,簡化代碼。

權限子產品我們涉及到的使用者管理、機構管理、角色管理、菜單管理、功能管理、記錄檔、登入日志等業務類,那麼這些類繼承BaseApi,就會具有相關的接口了,如下所示繼承關系。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

按照這個思路,我們在BaseApi的ES6類裡面定義了對應Web API基類裡面的操作方法,如下所示。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端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的異步線程調用方法,如下所示。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

 另外,由于我們的ES6接口定義,是基于TypeScript的,它的資料類型可以推斷出來,是以在編碼或者檢視對應屬性的時候,會有非常好的提示資訊,如上所示。

最後我們來驗證下實際的axios調用頁面的效果。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理

以及另外一個複雜一點的測試頁面展示。

基于SqlSugar的開發架構循序漸進介紹(10)-- 利用axios元件的封裝,實作對後端API資料的通路和基類的統一封裝處理