AXIOS介绍

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

在这里插入图片描述

axios是我们在vue项目中常见封装http请求相对受欢迎的,优点如下:

  1. 简单易用,api接近于jquery,比原生的fetch之类的简单,
  2. 浏览器兼容性好,都能兼容IE7,使用fetch就得自己处理兼容
  3. 通用性好,能在node和浏览器中使用
  4. api一致 稳定大牌,vue官网文档有推荐

以上几点呢,是大多数公司选择使用axios的主要原因,通过简单的了解我们应该知道axios是干嘛的了,请求这里指的是数据的传输,web端http属于单向的,你问了他就告诉你,服务端不会主动告诉你,这个就叫http请求,因为有协议的原因,所以这里很多人还是不能熟练掌握,具体请求具体传参,所以我准备专门介绍一下,各请求之间我是如何判断该何如传参的:

AXIOS使用 - 请求别名介绍

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[,config])
  • axios.head(url[, config])
  • axios.options(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])
  • axios.patch(url[, data[, config]])

url: 接口地址 data: 数据 config: 配置

  1. 先说一下request 他是一个任意类型的请求,你在mehod: ‘请求别名’ 比如:get post 都可以

  2. get 向指定资源请求数据,一般用来获取,查询资源信息,较不安全,幂等(幂等是对同一URL的多个请求应该返回同样的结果)的,只用来获取数据不会修改数据,其请求显示在url上,会被缓存,对请求长度有限制。和post是常用的提交方式。

  3. delete 请求服务器器删除Request-URI所标识的资源。

  4. head是需要向服务器索要与get一样的请求,但是不返回返回体,这个方法可以在不必传输整个响应内容的情况下,获取包含在响应消息头中的元信息。

  5. option返回给服务器针对特定资源所支持的请求方式,也可以利用向web服务器发送‘*’的请求来测试服务器的功能性。

  6. post 向指定资源提交数据进行请求处理,一般用来更新资源信息,非幂等,请求显示在请求体里面,不会被缓存,对请求长度无限制。

  7. put 向指定资源上传最新的内容

  8. patch方法用来更新部分资源,然而PATCH和POST都是非幂等的,POST请求服务器执行一个动作,多次请求会多次执行。PATCH提供的实体则需要根据程序或其它协议的定义,解析后在服务器上执行,以此来修改服务器上的数据。也就是说,PATCH请求是会执行某个程序的,如果重复提交,程序可能执行多次,对服务器上的资源就可能造成额外的影响POST方法和PATCH方法它们的实体部分都是结构化的数据,所以PAtch也是非幂等的。POST方法的实体结构一般是 multipart/form-data或 application/x-www-form-urlencoded而PATCH方法的实体结构则随其它规范定义。这和PUT方法的无结构实体相比就是最大的区别。

幂等:简单来说,幂等这个概念指的是多次同样的操作而不改变结果。幂等的概念广泛运用于各种分布式架构,由于网络延迟等原因,一个请求可能要多次重试,遇到这种情况就需要保证这个对应的请求接口是幂等的。另外还有类似银行转账的情形,就算多次请求也要保证对账户只做一次操作。

根据上面的介绍你应该大致都知道上面这些请求都是干嘛的了,那么下面进入正题

AXIOS使用 - 封装axios

axios封装,可以公用的部分,用统一的方法进行处理,避免代码冗余,在请求拦截中,处理特殊的传参方式,对相应部分在统一的返回处理,以下 的部分配置,需要结合axios官网配置阅读,里面如何安装以及更多,上代码先来看看

import axios from 'axios';
import store from '../store/index';
import errorCode from "@/utils/errorCode.js"; // 我们后台给提供错误码提示 呜呜...
import { stringify } from "qs";

/**
 * 请求失败后的错误统一处理
 */
const codeMessage = {
    200: '服务器成功返回请求的数据。',
    201: '新建或修改数据成功。',
    202: '一个请求已经进入后台排队(异步任务)。',
    204: '删除数据成功。',
    400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
    401: '用户没有权限(令牌、用户名、密码错误)。',
    403: '用户得到授权,但是访问是被禁止的。',
    404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
    406: '请求的格式不可得。',
    410: '请求的资源被永久删除,且不会再得到的。',
    422: '当创建一个对象时,发生一个验证错误。',
    500: '服务器发生错误,请检查服务器。',
    502: '网关错误。',
    503: '服务不可用,服务器暂时过载或维护。',
    504: '网关超时。',
}

// 创建axios实例
let instance = axios.create({
    // baseURL: process.env.VUE_APP_API_URL, // 域名 没配置因为我们是放在一个服务器下
    timeout: 1000 * 6,
    withCredentials: true, // 允许携带cookie中的参数
    transformResponse: [function transformResponse(data) {
        return jsonlint.parse(data) // jsonlint 针对javascript 不能解析超长数字类型数据转换成字符串
    }], // `transformResponse` 在传递给 then/catch 前,允许修改响应数据 
});

// 请求拦截 - 主要就是为了修改请求发出之前的请求头配置以及参数放置的位置
instance.interceptors.request.use(
    config => {
        //发请求前设置store中存储的接口请求状态
        /**
         * 设置对应接口请求状态对象
         * 然后分别在请求拦截,响应拦截中设置状态
         * 改变store中对应接口的请求状态
         */
        // console.log(config)

        store.dispatch("setLoading", {
            url: `${config.url}`,
            status: true,
        }); // 这个如果感兴趣,请查看我的vue 全局下的loading状态

        // 设置请求头
        if (config.method === "post" && !!config.params && config.params !== "") {
            config.headers = {
                'Accept': '*/*',
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            }
        }

        if (config.method === "put" && !!config.params && config.params !== "") {
            config.headers = {
                'Accept': '*/*',
                'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
            }
        }

        if (config.method === "delete") {
            if (!!config.params && !!config.params.data) {
                config.headers = {
                    'Accept': '*/*',
                    'Content-Type': 'application/json;charset=UTF-8',
                }
                config.data = config.params;
                config.params = ""
            }
        }
 		
 		// 这个配置项 是接口需要验证特殊加的
        if (!!config.userid) {
            config.headers.userid = config.userid
        }

        // console.log("请求配置", config)
        return config;
    },

    error => Promise.reject(error) //正常写这里应该没啥错误的
);

// 处理返回后Loading状态,url存在正常返回,反之是异常都会关闭Loading状态
function handleResponseLoading(url) {
    if (!!url) {
        let apiUrl = url;

        if (url.indexOf("?") != -1) {
            apiUrl = url.split("?")[0];
            // console.log(apiUrl);
        }

        // 如果成功则需要, 设置对应接口loading状态
        store.dispatch("setLoading", { url: apiUrl, status: false });
    } else {
        for (let l in store.state.Loading) {
            store.state.Loading[l] = false
        }
    }

}
// 响应拦截器
instance.interceptors.response.use(
    // 请求成功
    res => {
        // Get Put 请求中参数时拼接到地址栏的,所以对应的接口状态中应去掉?后的参数,才能取消请求状态
        handleResponseLoading(res.config.url)

        /*
            1.服务器会出现没有请求成功但无数据的情况,所要先判断当前返回是不是空,确认返回是空则需要提示服务异常
            2. data中返回的result代表结果 0 成功请求,如果不是需要新增一个阻断 errorMsg
                用于查询 errorCode 中存储的错误提示信息,并返回给 resolve 请求的回调
        */

        let data = {};
        // console.log("拦截", res.data)
        if (!!res.data) {
            if (res.status === 200 && res.data.result === 0) {
                data = res.data;
            } else {
                let code = errorCode.get(`${res.data.result}`);
                data = {
                    ...res.data,
                    errorMsg: `${res.data.result} * ${res.data.msg ? res.data.msg : code}`
                }
            }
        } else {
            data = {
                result: "error",
                errorMsg: `服务器网络异常,请您稍后重试!`
            }
        }

        return res.status === 200 ? Promise.resolve(data) : Promise.reject(data);
    },

    // 请求失败
    error => {
        console.log("========================>error", error)
        const { code, config, message, response } = error; // ES6 解构定义 等价于 const response = error.response、

        handleResponseLoading(config.url)
        /*
            1. 请求错误中,如果网络断开连接,也有response的存在,所以先判断是不是断网
            2. 然后开始判断有没有 response 的返回
            3. 有的话说明是请求状态的错误,则需要 codeMessage 中的提示信息(可能会根据错误码判断当前是否需要跳页面或重新登录)
            4. 没有 response 说明 连接网络超时或者发生其他暂时不清楚的错误
            
        */
        if (!window.navigator.onLine) { // onLine:表示是否连网,如果连网返回true,否则返回false
            // console.log("断网");
            return Promise.reject({
                errorMsg: `网络连接错误,请检查您的网络!`
            });
        } else {
            // console.log("没有断网");
            if (!!response) {
                // 请求已发出, 但是不在200的范围
                // console.log("错误返回存在response");
                const { response: { status, statusText, data: { msg = '服务器发生错误' } } } = error;
                const text = codeMessage[status] || statusText || msg;
                return Promise.reject({
                    errorMsg: text
                });
            } else {
                // console.log("错误返回不存在response");
                if (code === "ECONNABORTED" && message.indexOf('timeout') != -1)
                    return Promise.reject({
                        errorMsg: `网络请求超时,请检查您的网络后重试!`
                    });
                return Promise.reject({
                    errorMsg: `未知错误`
                });
            }
        }
    }
);

export {
    instance as axios, //通常情况下,`export`输出的变量就是本来的名字,但是可以使用`as`关键字重命名。
};

一般我们前端使用的接口都是后台以文档形式给我的,有的是写好的word ,有的是以postman文件发过来的,他们其实都已经明确了,请求的传参了,根据上面的axios封装我们继续说一下

AXIOS使用 - 请求头配置,传参配置

注意啊!!!所有的请求配置项可不都不一样,请看看上面 《请求别名介绍》

  1. 先了解HTTP 请求分为三个部分:状态行、请求头、消息主体,在HTTP协议的消息头中,通常使用Content-Type来表示传值的内容的格式,服务端根据Content-Type字段对获取消息主体的编码方式对消息解析。

  2. Content-Type 也是 MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息。也就是说 Content-Type 定义了客户端与服务端数据之间的交互形式,这样方便我们知道数据格式并处理。

例如: Content-Type: text/html;charset:utf-8;

常见的媒体格式类型如下:

text/html : HTML格式
text/plain :纯文本格式
text/xml :  XML格式
image/gif :gif图片格式
image/jpeg :jpg图片格式
image/png:png图片格式

以application开头的媒体格式类型:

   application/xhtml+xml :XHTML格式
   application/xml     : XML数据格式
   application/atom+xml  :Atom XML聚合格式    
   【常用】application/json    : 请求体中的数据会以json字符串的形式发送到后端
   application/pdf       :pdf格式  
   application/msword  : Word文档格式
   application/octet-stream : 二进制流数据(如常见的文件下载)
  【常用】 application/x-www-form-urlencoded :请求体中的数据会以普通表单形式(键值对)发送到后端

  【常用】multipart/form-data: 它会将请求体的数据处理为一条消息,以标签为单元,用分隔符分开。
  	既可以上传键值对,也可以上传文件。一般用于上传文件,例如头像,excel等文件

具体我是在这篇文章中找到的并且这里也详细说明了请求中各个参数配置的含义

post 请求封装

然后我们说请求方式,每种请求方式都有一个默认的 Content-Type
例如 post 请求默认的 ‘Content-Type’: ‘application/json;charset=utf-8’,参数放在请求体中 ,即是axios 的data中
你一定见过文档中 post 请求头 ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ 这样使用的,那么这种形式的时候我们就需要将参数放到 axios.config.params 中。
据我观察这时候post请求将参数以键值对形式在请求地址URL中,所以我在下面是这样配置的


if (config.method === "post" && !!config.params && config.params !== "") {
    config.headers = {
        'Accept': '*/*',
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    }
}

也就是,如果在你的请求配置中,你在params中添加了参数,我会在拦截中修改 ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ ,那么实际你的请求就应该这么写了,

1. post 默认传参 将【参数】放到请求体中
// 'Content-Type':  ‘application/json;charset=utf-8’,
axios.post(接口地址, 参数, {})

2. post 已键值对形式传参,【参数】放到 config.params中
// 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
axios.post(接口地址, {}, {
    params: {
        // 参数
    }
})

上面12具体情况我也没搞清楚什么时候使用,但是后台post请求大致就是这两种类型,即时你不知道后台要的是什么类型的方式试一下就知道了

3. 单独说一下 multipart/form-data,也是post中的常见类型, 这类型接口相对较少所以我直接在config中配置了,下面我有单独介绍一下这个方法
let params = new FormData()
    params.append('file', this.file) // 文件
    params.append('name', this.name) // 其他参数

	axios.post(接口地址, params, { 
		headers: {
			'Content-Type': 'multipart/form-data'
		}
	})

get 请求封装

get 请求 默认的 ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ ,而且一般也不会涉及到其他形式的,所以他的写法就是,get请求不玩幺蛾子,参数就是拼接到url上的,而且我也曾经看到过文章说 get 请求中’Content-Type’


// 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
axios.get(接口地址, {
    params: {
        // 参数
    }
})

put 请求封装

put 请求跟 post用法一致。别忘了别名替换一下axios.put() 啊。

delete 请求封装

delete 请求 他的默认 ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’ , 跟get是一样的,但是由于接口文档需要我们改成 ‘Content-Type’: ‘application/json;charset=utf-8’,还要把参数放在 请求体 data中,所以delete的请求也分成了 两种大致跟我上面 post 和 put差不多,区别就是 axios.delete的请求参数只有config 没有data这项,所以我在拦截中是这么做的

if (config.method === "delete") {
    if (!!config.params && !!config.params.data) {
        config.headers = {
            'Accept': '*/*',
            'Content-Type': 'application/json;charset=UTF-8',
        }
        config.data = config.params;
        config.params = ""
    }
}

上述是几种常见的额请求拦截处理。说完请求拦截处理,那我们看看请求的返回处理吧

在这里插入图片描述
上面这图呢,是一个正常的get请求的全部返回结果,包括请求的返回状态以及接口配置等等

首先我们正常返回这么多,你想用那部分常用的数据,这个结构来看就很繁琐了,而且封装里面有个 接口状态的存储

这里面我也是集成在 axios封装这部分了,所以这部分返回也需要统一处理,这样让你的请求看起来更加方便了

请求返回封装


// 请求成功
res => {
    // Get Put 请求中参数时拼接到地址栏的,所以对应的接口状态中应去掉?后的参数,才能取消请求状态
    handleResponseLoading(res.config.url)

    /*
        1.服务器会出现没有请求成功但无数据的情况,所要先判断当前返回是不是空,确认返回是空则需要提示服务异常
        2. data中返回的result代表结果 0 成功请求,如果不是需要新增一个阻断 errorMsg
            用于查询 errorCode 中存储的错误提示信息,并返回给 resolve 请求的回调
    */

    let data = {};
    // console.log("拦截", res.data)
    if (!!res.data) {
        if (res.status === 200 && res.data.result === 0) {
            data = res.data;
        } else {
            let code = errorCode.get(`${res.data.result}`);
            data = {
                ...res.data,
                errorMsg: `${res.data.result} * ${res.data.msg ? res.data.msg : code}`
            }
        }
    } else {
        data = {
            result: "error",
            errorMsg: `服务器网络异常,请您稍后重试!`
        }
    }

    return res.status === 200 ? Promise.resolve(data) : Promise.reject(data);
},

正常如果请求成功的话,res中一定会包括data部分,但是你不能排除部分请求中出现异常现象,返回中没有data

所以我做了一点提示效果,我对返回中有 data 跟没有 data的部分进行了处理,因为我们结果 result === 0 时表示我们可以正常拿到数据

所以我对result 不是 0 进行了错误码提示,把对应的错误提示文字,返给页面做提示。

对于失败的部分就是结果没有响应,断网啊,我页面上面封装写好了,失败嘛,就那几种我加了注释说明了一下

POST 上传文件 ‘Content-Type’: ‘multipart/form-data’

现在我们来说说这 post 的 上传文件 ‘Content-Type’: ‘multipart/form-data’ 请求中上传文件部分使用的请求头,参数放在请求体 data中的 form-data格式数据,而且对于文件上传,我们有需求添加一个进度条,由于这部分不是很多所有,我在上传方法中单独配置了这部分

// 上传头像
uploadHeadImg(data, progressCallBack) {
    return axios.post(apiUrl.global.uploadHeadImg, data, {
        headers: {
            'Accept': '*/*',
            'Content-Type': 'multipart/form-data',
        },
        
        onUploadProgress: function (progressEvent) {
            // 总数 progressEvent.total
            // 上传数 progressEvent.loaded
            progressCallBack(progressEvent.total, progressEvent.loaded)
        },
    });
},

// 考虑到有人可能不知道data里参数啥样的,我继续科普,formdata也是始终传参方式

// 首先创建一个FormData 对象
let formData = new FormData();

// 这里的data 就是 input type=file 中的数据,然后你把它传给 uploadHeadImg 中的data 就完成上传了
formData.append("file", data, '文件名');
formData.append('其他参数','其他参数值');

顺便说一下我用的是 antd-vue 的组件,别问我为啥,因为我菜!!!-.-

<a-upload-dragger
   	name="file"
    :multiple="false"
    :accept="accept"
    :before-upload="beforeUpload"
    :showUploadList="false"
    :reject="rejectFn"
>
    <div class="choice-tips" v-if="isChoiceArea">
        <div class="icon">
            <img :src="iconImg" />
        </div>
        <div class="text">{{tipsText}}照片拖到此处</div>
        <div class="hint"></div>
        <div class="btn">
            <Btn type="btn08C" text="选择您计算机上的照片"></Btn>
        </div>
    </div>
    <div v-if="!isChoiceArea">
        <Btn type="btn08C" text="重新上传"></Btn>
    </div>
</a-upload-dragger>

这个就是文件上传部分,其他的别的文件也是这么搞得。到此为止axios的大部分常见的用法就都说完了

总结

axios 传参放置方式两种 一种是 params,比如post、get、put请求都会用到 ,他的请求头是 ‘Content-Type’: ‘application/x-www-form-urlencoded;charset=UTF-8’

另一种就是 data 常见于 post put delete,一种请求头 ‘Content-Type’: ‘application/json;charset=UTF-8’,传参格式一般都是json对象,

post 上传文件请求头 ‘Content-Type’: ‘multipart/form-data’, 参数也在data中 是以 formdata格式传输的(binary);

以上就是我对axios的大致了解,虽然没有对http请求有着更深入的了解,而且对我本人来说,也没必要完全理解,上面这些内容完全可以胜任一个前端在工作中遇到大多数接口问题,希望这些理解可以帮助到有需要的你,点赞加关注,咱们共同进步啊。

欢迎大家评论,如果您有更好的看法和意见。
我不是大拿,我只是一个崇拜大拿的小菜 -.-

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐