美文网首页Vue
axios的核心

axios的核心

作者: 为光pig | 来源:发表于2021-01-12 13:53 被阅读0次

    1. Interceptors 拦截器

    axios 官网中对Interceptors 的使用方法如下: 用户可以通过 then 方法为请求添加回调,而拦截器中的回调将在 then 中的回调之前执行:

    // Add a request interceptor
    axios.interceptors.request.use(function (config) {
     // Do something before request is sent
     return config;
    }, function (error) {
     // Do something with request error
     return Promise.reject(error);
    });
    // Add a response interceptor
    axios.interceptors.response.use(function (response) {
     // Do something with response data
     return response;
    }, function (error) {
     // Do something with response error
     return Promise.reject(error);
    });
    

    之后你可能需要能移除 Interceptors :

    const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
    axios.interceptors.request.eject(myInterceptor);
    

    也可以为axios实例添加一个Interceptors:

    const instance = axios.create();
    instance.interceptors.request.use(function () {/*...*/});
    

    从其中的使用方法,我们可以知道 request 拦截器需要在请求之前执行,response 拦截器需要再请求之后执行。我们看一下axios的实现方式: 首先axios为拦截器定义了一个管理中心InterceptorManager:

    function InterceptorManager() {
     this.handlers = [];
    }
    InterceptorManager.prototype.use = function use(fulfilled, rejected) {
     this.handlers.push({
     fulfilled: fulfilled,
     rejected: rejected
     });
     return this.handlers.length - 1;
    };
    InterceptorManager.prototype.forEach = function forEach(fn) {
     utils.forEach(this.handlers, function forEachHandler(h) {
     if (h !== null) {
     fn(h);
     }
     });
    };
    

    2.当实例化Axios时,分别创建一个request 和一个response拦截器:

    function Axios(instanceConfig) {
     this.defaults = instanceConfig;
     this.interceptors = {
     request: new InterceptorManager(),
     response: new InterceptorManager()
     };
    }
    

    然后我们需要通过use来分别添加拦截器时,便将我们定义的resolve和reject收入对应的request和response中。在此之前,我们需要对Promise有一个简单的了解:

    Promise then 方法返回的是一个新的 Promise 实例(注意,不是原来那个 Promise 实例)。因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法。

    getJSON("/posts.json").then(function(json) {
     return json.post;
    }).then(function(post) {
     // ...
    });
    

    上面的代码使用then方法,依次指定了两个回调函数。第一个回调函数完成以后,会将返回结果作为参数,传入第二个回调函数。采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。

    接下来,看看request方法是如何实现拦截器功能的:

    Axios.prototype.request = function request(config) {
     ...
     var chain = [dispatchRequest, undefined];
     var promise = Promise.resolve(config);
     this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
     chain.unshift(interceptor.fulfilled, interceptor.rejected);
     });
     this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
     chain.push(interceptor.fulfilled, interceptor.rejected);
     });
     while (chain.length) {
     promise = promise.then(chain.shift(), chain.shift());
     }
     return promise;
    };
    

    首先定义了一个数组调用链chain,然后通过unshift将 request interceptors推入数组头部,将response interceptors推入数组尾部。最后通过while这样一个循环执行promise.then来达到链式调用。 来看一张图


    通过巧妙的利用unshift、push、shift等数组队列、栈方法,实现了请求拦截、执行请求、响应拦截的流程设定,注意无论是请求拦截还是响应拦截,越先添加的拦截器总是越“贴近”执行请求本身。

    3. 自动转换JSON数据

    在默认情况下,axios将会自动的将传入的data对象序列化为JSON字符串,将响应数据中的JSON字符串转换为JavaScript对象。这是一个非常实用的功能,但实现起来非常简单:

    var defaults = {
     ...
     transformRequest: [function transformRequest(data, headers) {
     normalizeHeaderName(headers, 'Content-Type');
     if (utils.isFormData(data) ||
     utils.isArrayBuffer(data) ||
     utils.isBuffer(data) ||
     utils.isStream(data) ||
     utils.isFile(data) ||
     utils.isBlob(data)
     ) {
     return data;
     }
     if (utils.isArrayBufferView(data)) {
     return data.buffer;
     }
     if (utils.isURLSearchParams(data)) {
     setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
     return data.toString();
     }
     // 这里对 object 做了转换
     if (utils.isObject(data)) {
     setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
     return JSON.stringify(data);
     }
     return data;
     }],
     transformResponse: [function transformResponse(data) {
     // 尝试转换 string -> json
     if (typeof data === 'string') {
     try {
     data = JSON.parse(data);
     } catch (e) { /* Ignore */ }
     }
     return data;
     }]
     ...
    }
    

    4. 支持客户端 XSRF 攻击防护

    XSRF 攻击,即“跨站请求伪造”(Cross Site Request Forgery)攻击。通过窃取用户cookie,让用户在本机(即拥有身份 cookie 的浏览器端)发起用户所不知道的请求。防护XSRF攻击的一种方法是设置特殊的 xsrf token,axios实现了对这种方法的支持:

    
    // 设置 xsrf 的 cookie 字段名和 header 字段名
    {
     // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
     xsrfCookieName: 'XSRF-TOKEN', // default
     // `xsrfHeaderName` is the name of the http header that carries the xsrf token value
     xsrfHeaderName: 'X-XSRF-TOKEN', // default
    }
    module.exports = function xhrAdapter(config) {
     return new Promise(function dispatchXhrRequest(resolve, reject) {
     ...
     // Add xsrf header
     // This is only done if running in a standard browser environment.
     // Specifically not if we're in a web worker, or react-native.
     if (utils.isStandardBrowserEnv()) {
     var cookies = require('./../helpers/cookies');
     // Add xsrf header
     // 如果允许cookie跨域或者同源,首先会从cookie中读取定义的token
     // 如果存在 xsrfValue 会将读取到的 xsrfValue 携带进入 requestHeaders 中
     var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
     cookies.read(config.xsrfCookieName) :
     undefined;
     if (xsrfValue) {
     requestHeaders[config.xsrfHeaderName] = xsrfValue;
     }
     }
     });
    };
    

    相关文章

      网友评论

        本文标题:axios的核心

        本文链接:https://www.haomeiwen.com/subject/dgpsaktx.html