1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        在TypeScript項(xiàng)目中搭配Axios封裝后端接口調(diào)用

        共 9393字,需瀏覽 19分鐘

         ·

        2024-03-30 10:00

        前言

        本來是想發(fā) next.js 開發(fā)筆記的,結(jié)果發(fā)現(xiàn)里面涉及了太多東西,還是拆分出來發(fā)吧~

        本文記錄一下在 TypeScript 項(xiàng)目里封裝 axios 的過程,之前在開發(fā) StarBlog-Admin 的時(shí)候已經(jīng)做了一次封裝,不過那時(shí)是 JavaScript ,跟 TypeScript 還是有些區(qū)別的。

        另外我在跟著 next.js 文檔開發(fā)的時(shí)候,注意到官方文檔推薦使用 @tanstack/react-query 來封裝請(qǐng)求類的操作,淺看了一下文檔之后感覺很不錯(cuò),接下來我會(huì)在項(xiàng)目里實(shí)踐。

        定義配置

        先創(chuàng)建一個(gè) global 配置,src/utilities/global.ts

              
              export default class Global {
          static baseUrl = process.env.NEXT_PUBLIC_BASE_URL
        }

        這是在 next.js 項(xiàng)目,可以用 next 規(guī)定的環(huán)境變量,其他項(xiàng)目可以自行修改。

        封裝 auth

        認(rèn)證這部分跟 axios 有點(diǎn)關(guān)系,但關(guān)系也不是很大,不過因?yàn)?axios 封裝里面需要用到,所以我也一并貼出來吧。

        創(chuàng)建 src/utilities/auth.ts 文件

              
              /**
         * 登錄信息
         */

        export interface LoginProps {
          token: string
          username: string
          expiration: string
        }

        /**
         * 認(rèn)證授權(quán)工具類
         */

        export default abstract class Auth {
          static get storage(): Storage | null {
            if (typeof window !== 'undefined') {
              return window.localStorage
            }
            return null
          }

          /**
             * 檢查是否已登錄
             * @return boolean
             */

          public static isLogin() {
            let token = this.storage?.getItem('token')
            let userName = this.storage?.getItem('user')

            if (!token || token.length === 0return false
            if (!userName || userName.length === 0return false
            return !this.isExpired();
          }

          /**
             * 檢查登錄是否過期
             * @return boolean
             */

          public static isExpired = () => {
            let expiration = this.storage?.getItem('expiration')
            if (expiration) {
              let now = new Date()
              let expirationTime = new Date(expiration)
              if (now > expirationTime) return true
            }

            return false
          }

          /**
             * 讀取保存的token
             * @return string
             */

          public static getToken = () => {
            return this.storage?.getItem('token')
          }

          /**
             * 保存登錄信息
             * @param props
             */

          public static login = (props: LoginProps) => {
            this.storage?.setItem('token', props.token)
            this.storage?.setItem('user', props.username)
            this.storage?.setItem('expiration', props.expiration)
          }

          /**
             * 注銷
             */

          public static logout = () => {
            this.storage?.removeItem('token')
            this.storage?.removeItem('user')
            this.storage?.removeItem('expiration')
          }
        }

        跟認(rèn)證有關(guān)的邏輯我都放在 Auth 類中了

        為了在 next.js 中可以愉快使用,還得做一些特別的處理,比如我增加了 storage 屬性,讀取的時(shí)候先判斷 window 是否存在。

        封裝 axios

        關(guān)于 API 的代碼我都放在 src/services 目錄下。

        創(chuàng)建 src/services/api.ts 文件,代碼比較長(zhǎng),分塊介紹,可以看到所有配置相比之前 JavaScript 版本的都多了配置,對(duì) IDE 自動(dòng)補(bǔ)全非常友好。

        先 import

              
              import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse, CreateAxiosDefaults} from "axios";
        import Global from '@/utilities/global'
        import Auth from "@/utilities/auth";

        Axios 配置

        定義一下 axios 的配置

              
              const config: CreateAxiosDefaults<any> = {
          method: 'get',
          // 基礎(chǔ)url前綴
          baseURL: `${Global.baseUrl}/`,
          // 請(qǐng)求頭信息
          headers: {
            'Content-Type''application/json;charset=UTF-8'
          },
          // 參數(shù)
          data: {},
          // 設(shè)置超時(shí)時(shí)間
          timeout: 10000,
          // 攜帶憑證
          withCredentials: true,
          // 返回?cái)?shù)據(jù)類型
          responseType: 'json'
        }

        統(tǒng)一接口返回值

        設(shè)置統(tǒng)一的接口返回值,這個(gè)和我在 StarBlog 后端里封裝的那套是一樣的,現(xiàn)在基本是我寫后端的標(biāo)準(zhǔn)返回值了,同時(shí)也發(fā)布了 CodeLab.Share nuget包,可以快捷的引入這個(gè)統(tǒng)一的返回值組件。

              
              // 統(tǒng)一接口返回值
        export interface ApiResponse {
          data: any
          errorData: any
          message: string
          statusCode: number
          successful: boolean
        }

        定義 ApiClient

        最后就是定義了 ApiClient 類,有點(diǎn)模仿 C# 的 HttpClient 內(nèi)味了

        這里面用到了 axios 的攔截器,發(fā)起請(qǐng)求的時(shí)候給 header 加上認(rèn)證信息,返回的時(shí)候看看有沒有錯(cuò)誤,如果是 401 unauthorized 的話就跳轉(zhuǎn)到登錄頁(yè)面。

              
              export class ApiClient {
          private readonly api: AxiosInstance

          constructor() {
            this.api = axios.create({
              ...config,
            })

            this.api.interceptors.request.use(
              config => {
                config.headers.Authorization = `Bearer ${Auth.getToken()}`
                return config
              },
              error => {
                return error
              })

            this.api.interceptors.response.use(
              response => {
                return response
              },
              error => {
                let reason = error
                if (error && error.response) {
                  if (error.response.data) {
                    reason = error.response.data
                    if (!reason.message) reason.message = error.message
                  }
                  if (error.response.status === 401) {
                    location.href = '/login'
                  }
                }

                return Promise.reject(reason)
              }
            )
          }

          public request(options: AxiosRequestConfig): Promise<ApiResponse> {
            return new Promise((resolve, reject) => {
              this.api(options).then((res: AxiosResponse<ApiResponse>) => {
                resolve(res.data)
                return false
              }).catch(error => {
                reject(error)
              })
            })
          }
        }

        export const api = new ApiClient()

        export default api

        代碼比之前我在 StarBlog-Admin 里的簡(jiǎn)單一些,我要盡可能用較少的代碼實(shí)現(xiàn)需要的功能。

        編寫具體接口調(diào)用

        所有的接口調(diào)用我都寫成 service (后端思維是這樣的)

        這里以發(fā)短信接口為例

        創(chuàng)建 src/services/common.ts 文件,從剛才定義的 api.ts 里面引入 ApiClient 的對(duì)象,直接調(diào)用 request 方法就完事了。

        參數(shù)類型是 AxiosRequestConfig ,不對(duì) axios 本身做什么修改,我感覺比之前用 Antd Pro 魔改的接口舒服一些。

              
              import {api} from './api'

        export class SmsChannel {
          static local = 0
          static aliyun = 1
          static tencent = 2
        }

        export default abstract class CommonService {
          public static getSmsCode(phone: string, channel: number = SmsChannel.local) {
            return api.request({
              url: `api/common/getSmsCode`,
              params: {phone, channel}
            })
          }
        }

        小結(jié)

        這樣封裝完比之前 StarBlog-Admin 的舒服很多,可惜之前那個(gè)項(xiàng)目用的是 vue2.x 似乎沒法用 TypeScript。

        就這樣吧,大部分內(nèi)容還是在 next.js 開發(fā)筆記中。

        參考資料

        • https://axios-http.com/zh/docs/interceptors
        • https://tanstack.com/query/latest/docs/react/overview


        瀏覽 55
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            色九九在线视频精品99 | 美女131爽爽爽 | 在线黄网站 | 成人免费无码无遮挡缓慢视频 | 欧美午夜精品免费视频app | 国产视频aa | 一道本一区二区日韩一区二区 | 老女人AV | 军人野外岔开腿呻吟bl | 好大好舒服视频 |