<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          在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 來封裝請求類的操作,淺看了一下文檔之后感覺很不錯(cuò),接下來我會在項(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 文件,代碼比較長,分塊介紹,可以看到所有配置相比之前 JavaScript 版本的都多了配置,對 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}/`,
            // 請求頭信息
            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ā)起請求的時(shí)候給 header 加上認(rèn)證信息,返回的時(shí)候看看有沒有錯(cuò)誤,如果是 401 unauthorized 的話就跳轉(zhuǎn)到登錄頁面。

                
                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 里的簡單一些,我要盡可能用較少的代碼實(shí)現(xiàn)需要的功能。

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

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

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

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

          參數(shù)類型是 AxiosRequestConfig ,不對 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)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一圾操逼视频 | 青青草偷拍视频 | 人人草人人舔 | 国产成人毛片18女人18精品 | 人人操,人人摸,人人透, |