axios 数据请求二次封装及实战案例

2 篇文章 0 订阅

目录

1、网络请求

2、项目实战应用


1、网络请求

项目中使用 axios 进行网络请求,Vue 脚手架默认没有安装,需要自行安装才能使用。

  • 安装 ( 生产环境依赖 )  "axios": "^0.24.0"

 npm i -S axios 

  • 设置统一请求域名

  • 反向代理 可以进行 跨域 解决

在项目根目录下面创建一个 vue.config.js 文件,写下如下代码

Vue 之 vue.config.js 配置文件_Mr.小灰狼_随笔-CSDN博客_vue.config.js

module.exports = {
  devServer: {
    overlay: false,
    // vue 项目代理请求
    proxy: {
      // 规则
      // axios 中相对地址开头的字符串
      '/api': {
        // 把相对地址中的域名 映射到 目标地址中
        // localhost:8080 => localhost:3000
        target: 'http://localhost:3000',
        // 修改 host 请求的域名为目标域名
        changeOrigin: true,
        // 请求 uri 和目标 uri 有一个对应关系
        // 请求 /api/login ==> 目标 /v1/api/login
        pathRewrite: {
          '^/api': '/v1/api'
        }
      }
    }
  }
}

代理配置 :

这里 , 此处 配置 反向代理 文件内 会有一个 小坑 : 

因为 匹配 的 规则 为  express.use  模糊匹配 , 匹配成功后就会停止向下匹配了 ,

所以匹配的 请求前缀 尽量 不要一样 ,  

比如 你写的  ' / api '   ,  只要你的请求地址内存在  / api  , 它在匹配上之后就会走对象内的程序代码了 , 就不会再向下执行 , 

所以为了预防此问题 , 所以 最好是把 你的 请求前缀 改成  ' ^/api '  ( 以 /api 开头的 )

这样的写法会更严谨 , 防止模糊匹配钻了漏洞

否则即使你下面 再怎么写的 pathRewrite ( 路径重写 ) , 也会失去效果 , 将你的重写路径依旧会带在你的请求地址里 , 并没有给过滤掉 , 导致请求一直 404 失败

所以 , 我们一是要注意咱们的 请求前缀不要一样 ,

二就是我们  最好 将前缀的写法写的更加严谨一些 

 网络请求模块化 : 


关于 axios 中设置 axios.defaults.withCredentials 的问题

withCredentials : 默认 情况下 ,跨源 请求 不提供 凭据

( cookie 、HTTP 认证 及 客户端 SSL 证明 等 ) 。

通过将 withCredentials 属性 设置 为 true ,可以指定某个请求应该发送凭据 。

默认值 为 false 。不提供
true  : 在跨域请求时 ,会 携带用户凭证
false :在跨域请求时 ,不会 携带用户凭证 ;返回的 response 里也会忽略 cookie
当配置了 withCredentials = true时 ,

必须 在 后端 增加  response  头信息  Access-Control-Allow-Origin  ,

且必须 指定 域名 ,而不能 指定 为  *

如果 后端 需要 带  cookie  过去 , 前端需要设置为  true


2、项目实战应用

二次封装 axios

项目  /  src  /  utils  /  request.js

// 导入 axios
import axios from "axios";
import { Message } from "element-ui";
// import env from '@/config/environment'
import Vue from "vue";

// 全局的 axios 默认值
// 设置请求的基础域名
// axios.defaults.baseURL = process.env.DW_API_URL
// axios.defaults.baseURL = baseurl
// 请求超时时间
axios.defaults.timeout = 10000;
// 在跨域请求时,会携带用户凭证( 是否允许请求携带 Cookie )
axios.defaults.withCredentials = true; // 后端需要带 cookie 过去

// post 请求头
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'

console.log(process.env, "VUE_APP_URL");
const service = axios.create({
  // baseURL: process.env.NODE_ENV === 'production' ? process.env.VUE_APP_BASE_API : 'api',  // api 的 base_url
  // baseURL: '/dev-api/', 设置基础路径
  // axios 中请求配置有 baseURL 选项,表示请求 URL 公共部分
  // baseURL: process.env.VUE_APP_BASE_URL,    // url = base url + request url
  // baseURL: baseurl,
  // 请求超时时间
  // timeout: env.envTimeout,
  timeout: 10000, // request timeout 设置最大响应时间,10秒
});

// request 请求拦截器
service.interceptors.request.use(
  (config) => {
    config.headers["Content-Type"] = "application/json";
    console.log(config, "config");
    // config.timeout = 10000
    return config;
  },
  (error) => {
    // 请求失败拦截
    console.log("request-error:" + error); // for debug
    return Promise.error(error);
  }
);

// response 响应拦截器
service.interceptors.response.use(
  (response) => {
    if (response.status === 200) {
      return response.data;
    } else {
      return Promise.reject(response);
    }
  },
  (error) => {
    // 服务器状态码不是200的情况
    let { message } = error;
    if (message.includes("Network Error")) {
      message = "后端接口连接异常";
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    } else if (message.includes("Request failed with status code")) {
      const code = message.substr(message.length - 3);
      if (code === "401" || code === "403") {
        message = null;
        if (code === "401") {
          // 401 表示没有权限(令牌, 用户名, 密码错误)
          // msgBoxNoLogin();
        } else if (code === "403") {
          // 403 表示用户有权限, 只是访问时被禁止的(可以理解为, 用户有权限, 但是某些目录禁止访问)
          // msgBoxNoRole();
          message = "您没有权限访问该目录"
        }
      } else {
        message = "系统接口" + message.substr(message.length - 3) + "异常";
        console.log(message, 'message');
      }
    }
    if (message !== null) {
      Message.error({
        message: message,
      });
    }
    return Promise.reject(error);
  }
);

// response 响应拦截器
/* service.interceptors.response.use(
  (response) => {
    console.debug("response");
    console.debug(response);
    if (response.status === 200) {
      const { data } = response;
      if (data.hasOwnProperty("resultCode")) {
        if (data.resultCode === 401) {
          msgBoxNoLogin();
        } else if (data.resultCode === 403) {
          msgBoxNoRole();
        } else if (data.resultCode !== 200) {
          let message = `异常码:${data.resultCode}`;
          if (data.hasOwnProperty("resultMsg")) {
            message = `${data.resultMsg},异常码:${data.resultCode}`;
          }
          msgError(message);
        }
      }
      return Promise.resolve(response);
    } else {
      return Promise.reject(response);
    }
  },
  (error) => {
    // 服务器状态码不是200的情况
    console.log("err" + error);
    let { message } = error;
    if (message.includes("Network Error")) {
      message = "后端接口连接异常";
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    } else if (message.includes("Request failed with status code")) {
      const code = message.substr(message.length - 3);
      if (code === "401" || code === "403") {
        message = null;
        if (code === "401") {
          msgBoxNoLogin();
        } else if (code === "403") {
          msgBoxNoRole();
        }
      } else {
        message = "系统接口" + message.substr(message.length - 3) + "异常";
      }
    }
    if (message != null) {
      msgError(message);
    }
    return Promise.reject(error);
  }
); */

// Vue.prototype.$http = service;

// export default service;

/**
 * 封装后的 get 请求方法
 * @param {string} url 请求路径
 * @param {object} data 请求参数
 * @param {object} params 用户自定义设置(参数)
 * @returns
 */
// GET 请求方法
export const get = (url, params = {}) => {
  let str = "?";
  Object.keys(params).forEach((item) => {
    str += `${item}=${encodeURI(params[item])}&`;
  });
  str = str.slice(0, -1);
  return service.get(url + str);
};

// POST 请求方法
export const post = (
  url,
  data = {},
  config = {
    headers: {
      "Content-Type": "application/json",
    },
  }
) => service.post(url, data, config);

模块化拆分

项目  /  src  /  config  /  api  /  listConfig.js

/* 网络 api 接口的基础配置 */
// 网络请求 的 地址信息
export default {
  getMattersList: '/api/survey/surveylist',    // 列表页所有事项数据
  saveSurveyName: '/api/surveyDesign/xxxx',    // 保存问卷名称
  saveAllQuestion: '/api/xxxxxxx/xxxxxxxx',    // 新建保存接口(全部)
  selectSurveyDetial: '/xxx/xxxxxxx/xxxxx',    // 预览
  xxxxXxxxxx: 'xxxxxxx/xxxxxxxxxx/xxxxxxx',    // XXXXXXXX
  
}

项目  /  src  /  config  /  environment.js

/ *  网站环境配置  * /

export default {

    envTimeout :  10000 , 

}


项目  /  src  /  api  / listApi.js

/* 网络 api 接口 */
// 请求网络方式
// 导入封装好的方法
import { get, post } from '../utils/request'
// 导入网络请求的地址信息
import config from '../config/api/listConfig'

// 首页问卷事项列表
export const getMyListApi = async (data) => await post(config.getMyList, data)
// 详情
export const getAnswerApi = async (params) => await get(config.getAnswer, params)
// 提交接口
export const saveAnswerApi = async (data) => await post(config.saveAnswerApi, data)
// 所有事项数据
export const getListApi = async (data) => await post(config.getList, data)
// 场景共享数据
export const getModelListApi = async (data) => await post(config.getModelList, data)

/* // 首页问卷事项列表
export const getMySurveylistApi = async (data) => {
  let ret = await post(config.getMySurveylist, data)
  return ret
}
// 首页 => 问卷详情
export const getExcuteAnswerApi = async (params) => {
  let ret = await get(config.getExcuteAnswer, params)
  return ret
}
// 问答页提交接口
export const saveAnswerApi = async (data) => {
  let ret = await post(config.saveAnswerApi, data)
  return ret
}
// statisticalManagement_所有事项数据
export const getSurveylistApi = async (data) => {
  let ret = await post(config.getSurveylist, data)
  return ret
}
// statisticalManagement_场景共享数据
export const getModelSurveylistApi = async (data) => {
  let ret = await post(config.getModelSurveylist, data)
  return ret
} */

Vue 组件内使用

 项目  /  src  /  views  /  home  /  index.vue

<template>
  <div class="box" style="overflow: auto">
    <ul
      v-if="tableList.length > 0"
      v-infinite-scroll="load"
      class="list"
      infinite-scroll-disabled="disabled"
    >
      <li v-for="(item, index) in tableList" :key="index">
        <div class="surveyName" v-text="item.surveyName"></div>
        <div class="info">
          <span class="date" v-text="item.createDate"></span>
          <span class="author" v-text="item.userName"></span>
        </div>
      </li>
    </ul>
    <p v-else>暂无数据</p>
    <p v-if="tableList.length > 0 && loading">加载中 ...</p>
    <p v-if="tableList.length > 0 && noMore">已加载全部</p>
  </div>
</template>

<script>
import { getMattersListApi } from "@/api/listApi";
export default {
  data() {
    return {
      loading: false,
      tableList: [], // 后端返回来的数据
      userId: "000", // 用户 Id
      pageData: {
        total: 0, // 总条数
        totalPages: 0, // 总页数
        pageSize: 10, // 一页显示的条数
        currentPage: 1, // 当前页数  起始页
      },
    };
  },
  created() {
    this.getList();
  },
  computed: {
    noMore() {
      // 当起始页大于总页数时停止加载
      return this.pageData.currentPage >= this.pageData.totalPages;
    },
    disabled() {
      return this.loading || this.noMore;
    },
  },
  methods: {
    // 获取列表数据的请求
    async getList() {
      let data = {
        userId: this.userId, // 用户 Id
        updateTime: this.pageData.currentPage, // 页数
        fetchNum: this.pageData.pageSize, // 一页显示的条数
      };
      let res = await getMattersListApi(data);
      if (res.code === "0") {
        this.pageData.total = res.result.totalCount;
        this.pageData.totalPages = res.result.totalPage;
        if (res.result.listItems && res.result.listItems.length > 0) {
          this.tableList = this.tableList.concat(res.result.listItems);
        }
      }
    },
    load() {
      this.loading = true;
      // 滑到底部时执行
      // 请求服务器数据时, 换成请求函数
      setTimeout(() => {
        this.pageData.currentPage++;
        this.getList();
      }, 2000);
    },
  },
};
</script>

<style lang="scss" scoped>
</style>
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值