最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Axios 源码解析

    正文概述 掘金(Daniel-Fang)   2021-02-22   885

    Features

    • Make XMLHttpRequests from the browser
    • Make http requests from node.js
    • Supports the Promise API
    • Intercept request and response
    • Transform request and response data
    • Cancel requests
    • Automatic transforms for JSON data
    • Client side support for protecting against XSRF

    Config order of precedence

    以下优先级依次递增

    • axios默认配置,具体在lib/defaults.js
    • global axios defaults
    axios.defaults.baseURL = 'https://api.example.com';
    
    • Custom instance defaults
    const instance = axiso.create({
        baseURL: 'https://api.example.com'
    });
    
    • request config
    instance.get('/users', {
        baseURL: 'https://api.github.com'
    })
    

    Supports the Promise API

    Usage

    // Promise 
    instance.get('/users').then(res => {
        console.log(res);
    })
    // Async | Await
    const res = await instance.get('/users');
    console.log(res);
    

    Source

    // 以 adapter/xhr.js举例
    function xhrAdapter (config) {
        return new Promise(function dispatchXhrRequest(resolve, reject) {
            let requestData = config.data;
            let request = new XMLHttpRequest();
            request.open(config.method.toUpperCase(), URL, true); // 请求尚未发送
            request.timeout = config.timeout;
            request.onreadystatechange = function handleLoad() {
                if (!request || request.readyState !== 4) {
                    return;
                }
                const response = {
                    data,
                    status: request.status,
                    statusText: request.statusText,
                    headers,
                    config,
                    request
                };
                settle(resolve, reject, response);
                request = null;
            }
            if (!requestData) {
                requestData = null;
            }
            // send the request
            request.send(requestData);
        });
    }
    // Resolve or reject a Promise based on response status
    function settle(resolve, reject, response) {
        const validateStatus = response.config.validateStatus;
        if (!response.status || !validateStatus || validateStatus(response.status)) {
            resolve(response);
        } else {
            reject(createError('...'));
        }
    }
    

    Adapter

    Usage

    const res = await instance.get('/users', {
        adapter: function customAdapter(config) {
            return fetch(config.url);
        }
    });
    

    Source

    function dispatchRequest (config) {
        const adapter = config.adapter || defaults.adapter; // 配置中可以自定义适配器,优先级 自定义适配器 > 默认适配器
        return adapter(config).then(function onAdapterResolution(response) {
            // ...
        }, function onAdapterRejection(reason) {
        	// ...
        })
    }
    const defaults = {
        adapter: getDefaultAdapter()
    };
    // 根据环境,使用不同适配器
    function getDefaultAdapter () {
        let adapter;
        if (typeof XMLHttpRequest !== 'undefined') {
            // For browsers use XHR adapter
            adapter = require('./adapter/xhr');    
        } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
        	// For node use HTTP adapter
            adapter = require('./adapter/http');
        }
        return adapter;
    }
    

    Transform request and response data

    Usage

    const res = await instance.get('/user', {
        transformRequest: [function (data, headers) {
            // Do whatever you want to transform the data
            return data;
        }],
        transformResponse: function (data, headers) {
            // Do whatever you want to transform the data
        	return JSON.parse(data);
        }
    });
    

    Source

    function dispatchRequest(config) {
        // Transform request data
        config.data = transformData(config.data, config.headers, config.transformRequest); // 自定义请求转换函数
        return adapter(config).then(function onAdapterResolution(response) {
            // Transform response data
            response.data = transformData(response.data, response.headers, config.transformResponse);
            return response;
        }, function onAdapterRejection(reason) {
            if (!isCancel(reason) {
                if (reason && reason.response) {
                    reason.response.data = transformData(reason.response.data, reason.response.headers, config.transformResponse);
                }
            })
        })
    }
    /*
     * @param {Array|Function} fns:A single function or Array of functions to transform data by data and headers
     */
    function transformData(data, headers, fns) {
        utils.forEach(fns, function transform(fn) {
            data = fn(data, headers);
        });
        return data;
    }
    

    Automatic transforms for JSON data

    Source

    const defaults = {
        transformResponse: [function transformResponse(data) {
            // 自动进行JSON字符串解析
            if (typeof data === 'string') {
                data = JSON.parse(data);
            }
            return data;
        }]
    };
    

    Interceptors

    Usage

    // 拦截器注册
    // 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);
    });
    

    Source

    // 拦截器收集 [lib/core/InterceptorManager.js](https://github.com/axios/axios/blob/master/lib/core/InterceptorManager.js)
    InterceptorManager.prototype.use = function use(fulfilled, rejected) {
        this.handlers.push({
            fulfilled: fulfilled,
            rejected: rejected
        });
        return this.handlers.length - 1;
    };
    // 拦截器触发
    Axios.prototype.request = function request(config) {
        const chain = [dispatchRequest, undefined];
        const 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);
        });    
        // chain = [requestFulfilled, requestRejected, dispatchRequest, undefined, responseFulfilled, responseRejected]
        while(chain.length) {
            promise = promise.then(chain.shift(), chain.shift());
        }
        return promise;
    }
    // 拦截器顺序:注册 => 收集 => 触发
    // 触发顺序:请求拦截器 => 请求 => 响应拦截器
    

    Cancel Requests

    Usage

    // 发送请求时携带
    const CancelToken = axios.CancelToken;
    const source = CancelToken.source();
    instance.get('/user/12345', {
      cancelToken: source.token // 只能叫cancelToken 名字不能改
    });
    // 取消请求
    source.cancel();
    

    Source

    function CancelToken(executor) {
        let resolvePromise;
        this.promise = new Promise(resolve => {
            resolvePromise = resolve;
        });
        const token = this;
        executor(function cancel(message) {
        	if (token.reason) return;
            token.reason = new Cancel(message);
            resolvePromise(token.reason)
        });
    }
    /**
     * Returns an object that contains a new `CancelToken` and a function that, when called,
     * cancels the `CancelToken`.
     */
    CancelToken.source = function source() {
      let cancel; // cancel的值就是上述function cancel方法,执行cancel方法的时候,执行resolvePromise方法
      const token = new CancelToken(function executor(c) {
          cancel = c;
      });
      return {
          token: token,
          cancel: cancel
      };
    };
    // xhr.js 或者 http.js
    if (config.cancelToken) {
          // Handle cancellation
          config.cancelToken.promise.then(function onCanceled(cancel) {
              if (!request) {
                  return;
              }
              request.abort();
              reject(cancel);
              // Clean up request
              request = null;
          });
    }
    

    XSRF

    XSRF又称CSRF(Cross-site request forgery),是一种劫持受信任用户向服务器发送非预期请求的攻击方式。通常情况下,CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器,从而在并未授权的情况下执行在权限保护之下的操作。一般通过以下三种方式防范:

    • 验证码
    • Referer Check
    • Token

    axios库采取了在请求中传入token,防范XSRF攻击。

    // axios 默认配置,具体使用时传入自定义xsrfCookieName、xsrfHeaderName
    const defaults = {
        xsrfCookieName: 'XSRF-TOKEN',
        xsrfHeaderName: 'X-XSRF-TOKEN'
    };
    // add XSRF header
    if (utils.isStandardBrowserEnv()) {
        const xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : undefined;
        if (xsrfValue) {
        	requestHeaders[config.xsrfHeaderName] = xsrfValue;
        }
    }
    

    参考

    • 学习 axios:封装一个结构清晰的 Fetch 库
    • 浅说 XSS 和 CSRF
    • axios README

    起源地下载网 » Axios 源码解析

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元