Administrator
Administrator
Published on 2025-01-06 / 0 Visits
0
0

Vue3系列教程-Http请求封装(K)

在前端开发中,网络请求是非常常见的操作。企业级项目都要求对网络请求的样板代码进一步封装后才能使用,因为封装之后的代码使用起来更简洁,更方便。同时封装之后的代码可屏蔽具体的依赖库,方便替换。可屏蔽URL,可统一拦截携带token,可统一响应消息等

Fetch API 的基本用法
fetch('https://jsonplaceholder.typicode.com/todos')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));

在上面的代码中,我们使用了 fetch 函数来发送一个 GET 请求。fetch 函数返回一个 Promise 对象,我们可以使用 .then 方法来处理响应。在这个例子中,我们首先把响应转换为 JSON 格式,然后打印出数据。如果发生错误,我们使用 .catch 方法来捕获并处理错误。

// fetch发送post请求,携带token的用法
    fetch(url, {
      method: "POST",
      headers: {
        "Accept": "*/*",
        "Content-Type": "application/json;charset=utf-8",
        "X-Custom-Token": localStorage.getItem("access-token")
      },
     body:JSON.stringify(data)
    }).then(res => res.json()).then(data => {
      console.log(data)
    }).catch(error => {
      console.log(error.msg)
    })
  }

在上面的代码中,我们使用了 fetch 函数来发送一个 POST 请求。POST请求通常携带的参数都是Json类型,所以必须在请求头设置 “Content-Type”: “application/json;charset=utf-8”,意思是说,这次请求的数据内容类型为JSON,且是“utf-8”编码,支持中文。为了保证能够顺利的访问到对应的接口,通常还要携带“token”凭证,以确保我们身份的合法性,这样我们在请求头中,就需要添加类似代码 “X-Custom-Token”: localStorage.getItem(“access-token”) ,X-Custom-Token 是前后端双方约定的请求头“字段”,用来在请求头众多数据中,快速找到token用的。

以上样板代码,如果没有封装,那么预示着项目中每个网络请求的地方,都要写这么啰嗦的样板代码,那是难以维护的,且相当恐怖的,所以封装简化势在必行。

Fetch 的二次封装

由于fetch函数,没有提供向axios 那样的拦截器功能,所以对Fetch函数的封装,就是用自定义函数request简单的包一下。

注意:在src目录下 创建utils目录,然后创建request.js或request.ts 文件


//接口地址   也可以通过环境文件注入进来
const baseURL = 'http://localhost:3000'

/**
 * 将对象转成 a=1&b=2的形式
 * @param obj 对象
 */
function obj2String(obj, arr = [], idx = 0) {
  for (let item in obj) {
    arr[idx++] = [item, obj[item]]
  }
  return new URLSearchParams(arr).toString()
}

/**
 * 请求封装
 * @param url
 * @param params
 * @param method
 * @returns {Promise<unknown>}
 */
export default (url, params, method = 'GET') => {
  return new Promise((resolve, reject) => {
    let initObj = {
      method: method,
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'X-Custom-Token': localStorage.getItem('access-token')
      }
    }
    if (method.toUpperCase() === 'GET') {
      const searchStr = obj2String(params)
      // GET请求,拼接url
      if(searchStr){
        url += '?' + searchStr
      }

    } else {
      //POST或PUT请求  增加 application/json
      Object.assign(initObj.headers, { 'Content-Type': 'application/json;charset=utf-8' })
      initObj = {
        ...initObj,
        body: JSON.stringify(params)
      }
    }
    console.log(initObj)
    fetch(baseURL + url, initObj)
      .then((res) => res.json())
      .then((res) => {
        //res.code 必须和后端约定好 统一响应格式 包含{code:"",message:"",data:""}
        switch (res.code) {
          case 200:
            console.log('成功了')
            resolve(res.data)
            break
          default: {
              // reject(res.message)  //可选
            alert(res.message)  //换成ElmentUI 弹框更好
          }
        }
      })
      .catch((err) => {
        console.log(err)
        alert('网络请求异常')  //换成ElmentUI 弹框更好
      })
  })
}


封装之后的使用案例
//首先要导入封装之后的文件
import request from '@/utils/request'

/**
 * 获取分类
 */
const getGroupsList = () => {
  //网络请求 ,不需要写ip地址 因为已经在封装体内置了
  request('/groups').then((data: any) => {
    console.log('结果', data)
  })
}

/**
 * 更新文章
 */
const updateArticle = () => {
  //网络请求 ,不需要写ip地址 因为已经在封装体内置了
  request(
    '/update',
    {
      id: '编号',
      content: '内容',
      author: '作者'
    },
    'POST'
  ).then((data: any) => {
    console.log('结果', data)
  })
}
其他网络请求框架(Axios)封装

Axios的官网:https://www.axios-http.cn/
说明:一样的封装思路,相比Fetch函数 Axios的封装就比较优雅,因为其本身提供了拦截器功能 ,可以很方便的进行请求拦截,携带Token等。

1、安装axios
$ npm install axios
2、依然在utils目录下,依然创建request.js文件
import axios from "axios";

import {ElNotification} from 'element-plus'
import {API_HOST,API_BASE_URL} from "@/config/index.js";
import { ElLoading } from 'element-plus'
// 创建Axios实例
const service = axios.create({
    baseURL:API_HOST+API_BASE_URL, // API基础地址
    timeout: 5000, // 请求超时时间
});

/**
 * 加载条
 * @type {{show(*): void, close(): void}}
 */
const loading = (() => {
    let loadingInstance = null;
    return {
        show(text) {
            loadingInstance = ElLoading.service({
                lock: true,
                text,
                background: "rgba(0, 0, 0, 0.6)",
            });
        },
        close() {
            loadingInstance.close();
        },
    };
})();





// 请求拦截器
service.interceptors.request.use(

    config => {
        loading.show("加载中");
        // 在发送请求之前做些什么,例如添加token
        config.headers['X-Custom-Token'] = sessionStorage.getItem('access-token');
        return config;
    },
    error => {
        // 对请求错误做点什么
        console.log(error);
        return Promise.reject(error);
    }
);

// 响应拦截器
service.interceptors.response.use(
    response => {
        loading.close();
        const result = response.data
        console.log('请求接口的结果:',result)
        if (result.code!==200) {
            ElNotification({
                title: 'Warning',
                message: result.message||'请求结果不合理',
                type: 'warning',
            })
            return Promise.reject(result.data)
        }
        return result.data; //取出最终数据
    },
    error => {
        loading.close();
        ElNotification({
            title: 'Error',
            message: error.message||'接口请求出现错误',
            type: 'error',
        })
        return Promise.reject(error.response.data);
    }
);


export default service;

最后

如何跟服务端拦截器配合,请参考sprintboot拦截器相关篇章


Comment