記讓一個http請求支持攔截器

最近想用全js系統的寫一遍先後端學習一下,就建立了一套TODOList項目練手。當前僅寫完了後端demo,前端正在使用vue。而且準備之後再用react和flutter再寫一遍。
此項目
後端demo

前提

在寫練手項目的時候使用了Framework7這個移動端ui框架,由於這個框架的動畫寫的很厲害因此選擇了它。可是在使用過程當中,發現這個框架自帶的ajax請求庫特別簡單,因而參照axios手動封裝了一下,使其支持promise和攔截器。javascript

動手

廢話很少說,上代碼
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僞裝顯示一下請求進度vue

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指向會有問題,須要單獨處理java

相關文章
相關標籤/搜索