在TypeScript项目中搭配Axios封装后端接口调用
作者:DealiAxy
前言
本来是想发 next.js 开发笔记的,结果发现里面涉及了太多东西,还是拆分出来发吧~
本文记录一下在 TypeScript 项目里封装 axios 的过程,之前在开发 StarBlog-Admin 的时候已经做了一次封装,不过那时是 JavaScript ,跟 TypeScript 还是有些区别的。
另外我在跟着 next.js 文档开发的时候,注意到官方文档推荐使用 @tanstack/react-query
来封装请求类的操作,浅看了一下文档之后感觉很不错,接下来我会在项目里实践。
定义配置
先创建一个 global 配置,src/utilities/global.ts
export default class Global { static baseUrl = process.env.NEXT_PUBLIC_BASE_URL }
这是在 next.js 项目,可以用 next 规定的环境变量,其他项目可以自行修改。
封装 auth
认证这部分跟 axios 有点关系,但关系也不是很大,不过因为 axios 封装里面需要用到,所以我也一并贴出来吧。
创建 src/utilities/auth.ts
文件
/** * 登录信息 */ export interface LoginProps { token: string username: string expiration: string } /** * 认证授权工具类 */ 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 === 0) return false if (!userName || userName.length === 0) return 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') } }
跟认证有关的逻辑我都放在 Auth
类中了
为了在 next.js 中可以愉快使用,还得做一些特别的处理,比如我增加了 storage
属性,读取的时候先判断 window
是否存在。
封装 axios
关于 API 的代码我都放在 src/services
目录下。
创建 src/services/api.ts
文件,代码比较长,分块介绍,可以看到所有配置相比之前 JavaScript 版本的都多了配置,对 IDE 自动补全非常友好。
先 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', // 基础url前缀 baseURL: `${Global.baseUrl}/`, // 请求头信息 headers: { 'Content-Type': 'application/json;charset=UTF-8' }, // 参数 data: {}, // 设置超时时间 timeout: 10000, // 携带凭证 withCredentials: true, // 返回数据类型 responseType: 'json' }
统一接口返回值
设置统一的接口返回值,这个和我在 StarBlog 后端里封装的那套是一样的,现在基本是我写后端的标准返回值了,同时也发布了 CodeLab.Share
nuget包,可以快捷的引入这个统一的返回值组件。
// 统一接口返回值 export interface ApiResponse { data: any errorData: any message: string statusCode: number successful: boolean }
定义 ApiClient
最后就是定义了 ApiClient
类,有点模仿 C# 的 HttpClient
内味了
这里面用到了 axios 的拦截器,发起请求的时候给 header 加上认证信息,返回的时候看看有没有错误,如果是 401 unauthorized 的话就跳转到登录页面。
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 里的简单一些,我要尽可能用较少的代码实现需要的功能。
编写具体接口调用
所有的接口调用我都写成 service (后端思维是这样的)
这里以发短信接口为例
创建 src/services/common.ts
文件,从刚才定义的 api.ts
里面引入 ApiClient
的对象,直接调用 request
方法就完事了。
参数类型是 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} }) } }
小结
这样封装完比之前 StarBlog-Admin 的舒服很多,可惜之前那个项目用的是 vue2.x 似乎没法用 TypeScript。
就这样吧,大部分内容还是在 next.js 开发笔记中。
参考资料
到此这篇关于在TypeScript项目中搭配Axios封装后端接口调用的文章就介绍到这了,更多相关Axios封装后端接口调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!