记让一个http请求支持拦截器

最近想用全js系统的写一遍前后端学习一下,就创建了一套TODOList项目练手。当前仅写完了后端demo,前端正在使用vue。并且准备以后再用react和flutter再写一遍。

此项目

后端demo

前提

在写练手项目的时候使用了Framework7这个移动端ui框架,因为这个框架的动画写的很厉害所以选择了它。但是在使用过程中,发现这个框架自带的ajax请求库特别简单,于是参照axios手动封装了一下,使其支持promise和拦截器。

动手

废话不多说,上代码
comover.js

import { Request as F7Request } from "framework7";

// 将原始请求对象封装成Promise对象
function adapter(config) {
    return new Promise(function(resolve, reject) {
        F7Request({
            url: `${config.baseUrl}${config.url}`,
            method: config.method,
            headers: config.headers,
            data: config.data,
            success(data, status, xhr) {
                resolve({
                    data: JSON.parse(data),
                    status: status,
                    config: config,
                    xhr: xhr
                });
            },
            error(xhr, status) {
                let error = new Error(
                    `Request failed with status code ${status}`
                );
                error.xhr = xhr;
                reject(error);
            }
        });
    });
}

// 发送请求
function dispatchRequest(config) {
    return adapter(config).then(
        function onAdapterResolution(response) {
            return response;
        },
        function onAdapterRejection(reason) {
            return Promise.reject(reason);
        }
    );
}

export default class Comeover {
    interceptors = {};
    requestHandlers = [];
    responseHandlers = [];
    config = {};
    constructor(config = ({ baseUrl = "" } = {})) {
        const self = this;

        this.config = { ...config };

        this.interceptors = {
            request: {
                use(fulfilled, rejected) {
                    self.requestHandlers.push({
                        fulfilled,
                        rejected
                    });
                }
            },
            response: {
                use(fulfilled, rejected) {
                    self.responseHandlers.push({
                        fulfilled,
                        rejected
                    });
                }
            }
        };
        
        // ES6中class内方法运行时绑定上下文
        this.request = this.request.bind(this);
    }
    request(config) {
        // 合并默认config和发送请求时的config
        let inconfig = { ...this.config, ...config };
        
        // 创建Promise链
        let chain = [dispatchRequest, undefined];
        // 创建初始Promise链中传递的promise对象
        let promise = Promise.resolve(inconfig);
        
        // 将拦截器注入Promise链
        this.requestHandlers.forEach(interceptor => {
            chain.unshift(interceptor.fulfilled, interceptor.rejected);
        });
        this.responseHandlers.forEach(interceptor => {
            chain.push(interceptor.fulfilled, interceptor.rejected);
        });
        
        // 运行Promise链
        while (chain.length) {
            promise = promise.then(chain.shift(), chain.shift());
        }
        
        // 返回最终的promise对象
        return promise;
    }
}

使用

这个例子就是在所有请求前后使用nprogress假装显示一下请求进度

import Comeover from "./comeover";
import Np from "nprogress";

const baseUrl = process.env.NODE_ENV === "development" ? "" : /* 上线地址 */ "";

const comeover = new Comeover({ baseUrl });

comeover.interceptors.request.use(
    config => {
        Np.start();
        return config;
    },
    error => {
        Np.done();
        return Promise.reject(error);
    }
);
comeover.interceptors.response.use(
    response => {
        Np.done();
        return response;
    },
    error => {
        Np.done();
        return Promise.reject(error);
    }
);

export { request };

发请求

comeover.request({
    url: "/api/login",
    method: "post",
    data: {
        email: this.email,
        password: this.password
    }
})
    .then(({ data }) => {
        this.$store.commit("login", { token: data.message });
        router.back();
    })
    .catch(err => {
        app.dialog.alert("用户名或密码错误", "登陆失败");
    });

总结

还可以参照axios继续封装单独的get、post等等的方法,这个demo就不写了。
Promise链是个数组,然后把请求拦截器放到真正请求的前面,响应后的拦截器放在真请求的后面。然后以resolve在前,reject在后的顺序,成对循环注入到promise.then中。而真正请求的resovereject是写在dispatchRequest里的,所以dispatchRequest这里没有reject,要加一个undefined
ES6的实例化方法单独使用的时候this指向会有问题,需要单独处理

    原文作者:see311
    原文地址: https://segmentfault.com/a/1190000020163418
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞