基于axios的前端请求封装实践(工作向)

1,001 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情

一、需求场景

1.axios是基于promise的网络请求库,封装了请求方法。

2.在一些比较大型的项目中,请求接口数量众多,我们会把接口抽取出来或者按模块管理。

3.接口封装,调用简易为上。

二、header头Content-Type信息

在此对header头的信息不作赘述,不过在日常工作中,遇到http错误400,一般可以从前后端数据格式(入参和出参)是否不一致、数据是位于请求体或是请求头中、token的位置等角度排错。 Content-Type用于描述传输的数据的类型,告诉对应的客户端如何解码。 本文重点介绍以下几个与工作相关的属性

1.application/x-www-form-urlencoded

默认的post请求的提交方式,请求的数据会以键值对的形式拼接(key1=val1&key2=val2) 注意:get请求没有请求体,因此content-type设置了没有效果,且get请求的编码方式是各浏览器决定的

2.mulitPart/form-data 表单方式提交;最常见的的post的提交方式

3.application/json 序列化的JSON串

总结:

遇到要传JSON值,需要手动设置content-type :application/json

遇到需要传递From URL Encoded 格式的formData,需要手动设置 content-type:application/x-www-form-urlencoded,并且使用 qs.stringify (data) 将data转换为url格式,才能正常使用,或者将data转换为formData格式

遇到需要文件流 (Multipart From)格式的formData,需要 手动构建formData 数据,(new formData.append('a',1),...), 然后去掉所有的 content-type设置。也就是不对content-type进行设置。

介绍:qs序列化,将对象序列化为字符串

let data = {
     orgId: 1,
     systemName: ' 机构',
};

qs.stringify(data);       //'orgId=1&ststemName=机构'

上述内容了解即可,不必深究,只要知道参数是什么格式、参数放在什么位置即可。

三、实践

1.get、post请求统一处理

前端请求参数都为对象,因此在get请求中表现为把对象拼接在url后面,post请求则是将请求对象转换成formdata对象,以mulitPart/form-data格式传输

 function getParamSpell(url, param) {
    let paramString = [];
    for (let key in param) {
        if (param[key] !== undefined && param[key] !== null && param[key] !== '') {
            paramString.push(key + "=" + param[key])
        }
    }
    if (paramString.length > 0) {
        url = url + "?" + paramString.join("&")
    }
    return url
    }
    
 function dataFromTransfer(param) {
    let fromParam = new FormData();
    for (let key in param) {
        // 判断类型 '[object Object]'  代表对象    '[object Array]'代表数组
        if (param[key] !== undefined && param[key] !== null) {
            if (Object.prototype.toString.call(param[key]) === '[object Object]' || Object.prototype.toString.call(param[key]) === '[object Array]') {
                fromParam.append(key, JSON.stringify(param[key]));
            } else {
                fromParam.append(key, param[key]);
            }
        }
    }
    return fromParam;
}
    
    

get请求

    get(url, param, success, error, mockConfig) {
        this.getBase(url, param, {}, success, error, mockConfig);
    }
     // 将请求参数对象格式化为 key=val&&key2=val2的格式
    getBase(url, param, config, success, error, mockConfig) {
        url = getParamSpell(url, param);
        this.generateMock(url, 'GET', mockConfig)
        axios.get(url, config).then(response => {
            checkResult(response, success, error);
        }).catch((fault) => {
            errorResult(fault, error)
        })
    },
    
}

post请求

    post(url, param, success, error, mockConfig) {
        this.postBase(url, param, {}, success, error, mockConfig)
    },
    postBase(url, param, config, success, error, mockConfig) {
        this.generateMock(url, 'POST', mockConfig)
        axios.post(url, dataFromTransfer(param), config).then(response => {
            checkResult(response, success, error);
        }).catch((fault) => {
            errorResult(fault, error)
        });
    },

patch、put等请求不做其他特殊处理:

    patch(url, param, success, error, mockConfig) {
        this.generateMock(url, 'PATCH', mockConfig)
        axios.patch(url, dataFromTransfer(param)).then(response => {
            checkResult(response, success, error);
        }).catch((fault) => {
            errorResult(fault, error)
        })
    },

请求成功和失败的处理:

这里除了要判断网络请求状态,也要判断具体的业务状态码,具体情况根据业务决定

function errorResult(fault, error) {
    if (logError) {
        console.log("http error is :", fault)
    }
    if (error) {
        error('操作失败');
    }
}

//跳转登录
function goToPath() {
    let fullPath = window.document.location.href;
    let filePath = window.document.location.pathname;
    let pos = fullPath.indexOf(filePath);
    let host = fullPath.substring(0, pos);
    if (process.env.NODE_ENV === 'production') {
        // 正式环境跳转至主域名
        window.location.href = `${host}`
    } else {
        window.location.href = "http://www.baidu.com"
    }
}


function checkResult(response, success, error) {
    let statusCode = response.status;
    let responseData = response.data;
    //请求不成功
    if (200 !== statusCode) {
        if (error) {
            error(statusCode);
        }

        return
    }
    let responseCode = responseData.code
    //请求成功,请求数据正确
    if (0 === responseCode) {
        if (typeof success === 'function') {
            if (success) {
                success(responseData.data);
            }
        }
        return
    }
    //请求成功,文件类型请求
    if (typeof success === 'object' && 'file' === success.type) {
        if (success.callback) {
            success.callback(response);
        }
        return
    }
    //请求成功,请求有错误
    if (error) {
        error(responseData.msg, responseData.code);
        if (responseData.msg == '登录过期,请重新登录') {
            goToPath()
        }
    }

}

文件类型的请求:

    uploadFile(url, param, success, error, mockConfig) {
        this.postBase(url, param, {
            headers: {
                "Content-type": "multipart/form-data",
                "Access-Control-Allow-Origin": "*",
            }
        }, success, error, mockConfig)
    },
    
    downloadFile(url, param, success, error, fileName, mockConfig) {
        this.getBase(url, param, {
            responseType: 'blob',
        }, {
            type: 'file',
            callback: result => {
                if (success) {
                    success(true)
                }
                let url = window.URL.createObjectURL(result.data);
                let link = document.createElement('a');
                link.style.display = 'none';
                link.href = url;
                link.setAttribute('download', fileName);
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            }
        }, error, mockConfig);
    },

其余配置信息:

    // 是否输出错误信息
    set loggerStatus(status) {
        logger = status
    },
    // 请求的基准地址
    set apiHost(apiHost) {
        axios.defaults.baseURL = apiHost;
    },
    get apiHost() {
        return axios.defaults.baseURL;
    },
    set mockEnable(enable) {
        if (enable) {
            Mock = require('mockjs');
        }
    },
    // 设置请求头,一般用于携带token信息
    setCommonHeader(key, value) {
        if (!value.isNullOrEmpty()) {
            axios.defaults.headers.common[key] = value;
        } else {
            delete axios.defaults.headers.common[key];
        }
    },
    set timeout(timeout) {
        axios.defaults.timeout = timeout;
    },
    set mockTimeout(timeout) {
        if (Mock) {
            Mock.setup({
                timeout: timeout
            })
        }
    },

调用:

api地址:

www.tianqiapi.com/api?version…

    getWeatherTest(params, success) {
        http.get("/api", params,
            result => {
                if (success) {
                    success(result)
                }

            }, error => {
                if (success) {
                    success(error)
                }
            });
    }
    ————————————————————————————————————————————————————————————————————
    http.logErrorStatus = true;
    http.apiHost = "http://www.tianqiapi.com";
    let data = {
      version: "v9",
      appid: "23035354",
      appsecret: "8YvlPNrz",
    };
    api.testA.getWeatherTest(data, (res) => {
      console.log(res);
    },(error)=>{
        toast.error(error)
        }
    );

输出:

image.png