BCVP.VUE3系列第三课:基于泛型基类封装Axios拦截器

科技   科技   2024-09-17 18:42   北京  
BCVP 开发者社区出品

BCVP V3开发

数字化
服务化
绿色化



放假不停歇,趁着假期学习下VUE3相关的内容,一方面是自己保持活力,另一方面也是工作需要,本系列是我的自学教程,如果有从0开始学习VUE3的,可以跟着一起练习下,毕竟前端我也是泥腿子出身,这一系列会使用Vite、TS、Pinia、Element-Plus等新知识点,既是查漏补缺,也是知识分享。

代码地址:

https://github.com/anjoy8/bcvp.vue3.git

这是每篇文章一节课一个分支,方便大家学习,会慢慢的将blog.admin项目进行翻新,使用的后端接口还是BlogCore。

系列文章:

BCVP.VUE3系列第一课:项目初始化与核心知识点说明

BCVP.VUE3系列第二课:基于泛型基类封装Axios请求



0、本文介绍



通常,为了更规范地封装登录接口,并且处理全局的请求和响应逻辑,我们可以通过设置 axios 的拦截器来处理 request 和 response,包括自定义 token 的注入以及响应状态码的处理。

axiosInstance.ts 中我们封装了常用的 HTTP 请求方法:get、post、put 和 delete。

每个方法返回 AxiosResponse 中的 data,这样可以直接获取到返回的数据,而无需在调用时处理 response。

请求拦截器和响应拦截器保持不变,用于处理 token 注入和全局错误处理。

通过这种方式,我们可以在项目中更清晰地发起不同的 API 请求,并确保请求的规范性和可维护性。




1、设置 Axios 拦截器


在src文件夹下,创建utils文件夹,然后创建一个axiosInstance.ts实例并添加拦截器,处理请求和响应,并封装get、post、put和delete等四种请求。
import axios from 'axios';import type { InternalAxiosRequestConfig, AxiosResponse } from 'axios';import { useAuthStore } from '@/stores/auth';import router from '@/router';
// 创建 axios 实例const axiosInstance = axios.create({    baseURL: '',  // 替换为你的 API 基础 URL    timeout: 10000,  // 请求超时时间});
// 请求拦截器axiosInstance.interceptors.request.use(    (config: InternalAxiosRequestConfig) => {  // 使用 InternalAxiosRequestConfig 类型        const authStore = useAuthStore();        if (authStore.token) {            config.headers['Authorization'] = `Bearer ${authStore.token}`;  // 在请求头中添加 token        }        return config;    },    (error) => {        return Promise.reject(error);    });
// 响应拦截器axiosInstance.interceptors.response.use(    (response: AxiosResponse) => {        return response;    },    (error) => {        if (error.response) {            const { status } = error.response;            if (status === 401) {                // 未授权,跳转到登录页面                router.push({ name: 'login' });            } else if (status === 403) {                // 无权限访问,提示用户                console.error('无权限访问');            } else if (status === 500) {                // 服务器错误                console.error('服务器错误');            }        }        return Promise.reject(error);    });
// 封装 get 请求export const get = async <T>(url: string, params?: any): Promise<T> => {    const response: AxiosResponse<T> = await axiosInstance.get(url, { params });    return response.data;};
// 封装 post 请求export const post = async <T>(url: string, data?: any): Promise<T> => {    const response: AxiosResponse<T> = await axiosInstance.post(url, data);    return response.data;};
// 封装 put 请求export const put = async <T>(url: string, data?: any): Promise<T> => {    const response: AxiosResponse<T> = await axiosInstance.put(url, data);    return response.data;};
// 封装 delete 请求export const del = async <T>(url: string, params?: any): Promise<T> => {    const response: AxiosResponse<T> = await axiosInstance.delete(url, { params });    return response.data;};
export default axiosInstance;



2、完善登录请求


接下来,使用配置好的 axios 实例来封装登录请求:


import { get } from '@/utils/axiosInstance';
/** * 请求的入参接口 * @interface LoginRequest * @property {string} name - 用户名 * @property {string} pass - 密码 */export interface LoginRequest {  name: string;  pass: string;}
/** * 基础响应接口,使用泛型 T 来表示响应体 * @template T * @interface BaseResponse * @property {number} status - HTTP 响应状态码 * @property {boolean} success - 请求是否成功 * @property {string} msg - 响应的消息 * @property {string | null} [msgDev] - 开发用的详细信息,可能为空 * @property {T} response - 具体的响应数据 */export interface BaseResponse<T> {  status: number;  success: boolean;  msg: string;  msgDev?: string | null;  response: T;}
/** * 登录响应接口 * @interface LoginResponse * @property {boolean} success - 是否登录成功 * @property {string} token - JWT token * @property {number} expires_in - token 的有效时长(秒) * @property {string} token_type - token 类型,通常为 "Bearer" */export interface LoginResponse {  success: boolean;  token: string;  expires_in: number;  token_type: string;}
/** * 发起登录请求 * @function login * @param {LoginRequest} params - 登录请求的参数 * @returns {Promise<BaseResponse<LoginResponse>>} 返回一个包含登录响应数据的 Promise * @throws {Error} 请求失败时抛出错误 */export const login = async (params: LoginRequest): Promise<BaseResponse<LoginResponse>> => {  try {    const response = await get<BaseResponse<LoginResponse>>('/api/Login/JWTToken3.0', {      name: params.name,      pass: params.pass,    });    return response;  } catch (error) {    throw new Error('请求失败');  }};


这种写法对于习惯vue2的开发来说,还是不太舒服,直接用setup的语法糖更好理解一些:

<template>    <div class="login">        <h1>登录</h1>        <form @submit.prevent="onSubmit">            <div>                <label for="name">用户名</label>                <input v-model="loginForm.name" id="name" type="text" required />            </div>            <div>                <label for="pass">密码</label>                <input v-model="loginForm.pass" id="pass" type="password" required />            </div>            <button type="submit" :disabled="loading">                {{ loading ? '登录中...' : '登录' }}            </button>        </form>        <p v-if="errorMessage" class="error">{{ errorMessage }}</p>    </div></template>

<script setup lang="ts">import { ref } from 'vue';import { useRouter } from 'vue-router';import { login } from '@/api/loginApi';import type { LoginRequest, BaseResponse, LoginResponse } from '@/api/loginApi';
const router = useRouter();const loginForm = ref<LoginRequest>({    name: '',    pass: '',});const loading = ref(false);const errorMessage = ref<string | null>(null);
/** * 登录表单提交处理函数 */const onSubmit = async () => {    loading.value = true;    errorMessage.value = null;
    try {        const response: BaseResponse<LoginResponse> = await login(loginForm.value);
        if (response.success) {            // 登录成功,跳转到首页或者其他页面            router.push({ name: 'Home' });        } else {            // 登录失败,显示错误信息            errorMessage.value = response.msg;        }    } catch (error) {        // 请求错误处理        errorMessage.value = '登录失败,请重试';    } finally {        loading.value = false;    }};</script>
<style scoped>.login {    max-width: 400px;    margin: 0 auto;    padding: 1rem;}
.error {    color: red;    margin-top: 1rem;}</style>


修改地方如下:







通过设置 axios 的拦截器,我们能够在每次请求时自动注入 token,并且统一处理响应状态码,提高了代码的规范性和可维护性。同时,通过拦截器,所有 API 调用共享相同的请求和响应处理逻辑,避免重复代码。


下篇文章就开始正式写代码,包括前端样式和接口联调,借鉴第三方项目,实现完整的登录效果样式,也会开始慢慢讲BlogAdmin进行翻译到Vue3版本。

BCVP代码创新社
专注于 NetCore 相关技术栈的推广,致力于前后端之间的完全分离,从壹开始,让每一个程序员都能从这里学有所成。
 最新文章