阅读 506

Vue学习笔记(二)------axios学习

axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。

axios的功能和特征:
  • 从浏览器中创建XMLHttpRequests
  • 从node.js中创建http请求
  • 支持PromiseAPI
  • 拦截请求和响应
  • 转换请求数据和响应数据
  • 取消请求
  • 自动转化JSON数据
  • 客户端支持防御XSRF
在项目中安装axois

使用npm

npm   install  axios --save
复制代码

使用cdn

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
复制代码
使用demo
  • 执行GET请求,当执行GET方式时,如果携带请求参数,请求参数可以有两种方式:

    axios.get('/user?ID=12121').then((response)=> {
        //response
    }).catch((error)=> {
        //error
    });
    或者
    axios.get('/user',{
        params: {
            ID: 12121
        }
    }).then((response)=> {
        //response
    }).catch((error)=> {
        //error
    });
    复制代码
  • 执行POST请求

    axios.post('/user',{
        name: '12121'
    }).then((response) => {
        //reponse
    }).catch((error) => {
        //error
    });
    复制代码
  • 执行多个并发请求

function getUserAccount() {
  return axios.get('/user/12345');
}

function getUserPermissions() {
  return axios.get('/user/12345/permissions');
}

axios.all([getUserAccount(), getUserPermissions()]).then(
    axios.spread(function (acct, perms){
        //两个请求现在都执行完成
    })
);
复制代码

上面这种写法都是通过axios.get/post这种写法,这种写法稍微有点麻烦,可以通过这种写法,通过axios传递相关配置来创建请求。同时axios的默认的请求方式为GET方式。

axios({
    url: apiURL,
    method: method,
    timeout: timeout,
    data: data,
    params: params,
    headers: {
        'Content-Type': 'application/json',
        'token': window.sessionStore.getItem('token') || ''
    }
});
复制代码

当处理并发的方式时:

  • axios.all(iterable)
  • axios.spread(callback)
同时axios也支持通过自定义配置创建一个axios示例

语法: axios.create([config])

var instance = axios.create({
   baseURL: 'https://some-domain.com/api/',
   timeout: 1000,
   headers: {'X-Custom-Header': 'foobar'} 
});
复制代码

相关的请求配置

这些是创建请求时可以用的配置选项。只有 url 是必需的。如果没有指定 method,请求将默认使用 get 方法。

//'url'是用于请求服务器的URL
url: '/user',

//'method'是创建请求时使用的方法
method: 'get', //默认是get
    
//'baseUrl'将自动加在'url'前面,除非'url'是一个绝对url
//它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
baseURL: 'https://some-domain.com/api/',

//'transformRequest'允许向服务器发送前,修改请求数据
//只能用在put、post、patch这几个请求方法
//后面数组中的函数必须返回一个字符串,或ArrayBuffer,或Stream
transformRequest: [function (data){
   //对data进行任意转换处理
    return  data;
}],

//'transformResponse'在传递then/catch前,允许修改响应数据
transformResponse: [function (data){
   // 对data进行任意的转换
    return  data;
}],
    
//'headers'是即将被发送的自定义请求头
headers: {'X-Requested-With': 'XMLHttpRequest'},
    
//'params'是即将与请求一起发送的url参数
//必须是一个无格式对象(plain object)或 URLSearchParams对象
params: {
  ID: 12345        
},
// `paramsSerializer` 是一个负责 `params` 序列化的函数
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function(params) {
     return Qs.stringify(params, {arrayFormat: 'brackets'})
},
    
// 'data'是作为请求主体被发送的数据
// 只适用于这些请求方法(put,post,patch)
// 在没有设置 `transformRequest` 时,必须是以下类型之一:
// string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - 浏览器专属:FormData, File, Blob
// - Node 专属: Stream 
data: {
 name: '1212'       
},
    
// `timeout` 指定请求超时的毫秒数(0 表示无超时时间)
// 如果请求话费了超过 `timeout` 的时间,请求将被中断
timeout: 1000,
    
// `withCredentials` 表示跨域请求时是否需要使用凭证
withCredentials: false, // 默认的
    
// `adapter` 允许自定义处理请求,以使测试更轻松
// 返回一个 promise 并应用一个有效的响应 (查阅 [response docs](#response-api)).
adapter: function (config) {
   /* ... */
},

// `auth` 表示应该使用 HTTP 基础验证,并提供凭据
// 这将设置一个 `Authorization` 头,覆写掉现有的任意使用 `headers` 设置的自定义 `Authorization`头
auth: {
  username: 'janedoe',
  password: 's00pers3cret'
},
    
// 'responseType'表示服务器响应的数据类型,可以是arraybuffer,blob,document,json,text,stream
responseType: 'json',   
复制代码
响应结构

某个请求的响应包含以下信息:

{
  // `data` 由服务器提供的响应
  data: {},

  // `status` 来自服务器响应的 HTTP 状态码
  status: 200,

  // `statusText` 来自服务器响应的 HTTP 状态信息
  statusText: 'OK',

  // `headers` 服务器响应的头
  headers: {},

  // `config` 是为请求提供的配置信息
  config: {}
}
复制代码

在实际的开发过程中后台的接口会遵循RESTful规范,同时会自己封装数据的返回格式,例如是下面这种格式:

img

也就是说我们取出的数据是从response.data中去拿到,然后按一定的格式取,一般后台的数据的封装格式如下:

新建一个util的包:

在这里插å
¥å›¾ç‰‡æè¿°

MessageEntity类:

package com.ssm.util;
import lombok.Data;

@Data
public class MessageEntity<T> {
    private Integer code;
    private String msg;
    private T data;
    private Integer total;
}

复制代码

MessageCode类:

package com.ssm.util;

public enum MessageCode {

    SYSTEM_CODE(500, "系统错误");

    private Integer code;
    private String message;

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    MessageCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
复制代码

ResultForma类:

package com.ssm.util;

public class ResultFormat {

    public static MessageEntity success() {
        return success(null);
    }

    public static <T> MessageEntity<T> success(T t){
        MessageEntity msg = new MessageEntity();
        msg.setCode(200);
        msg.setMsg("操作成功");
        msg.setData(t);
        return msg;
    }

    public static  MessageEntity error(MessageCode message) {
        MessageEntity msg = new MessageEntity();
        msg.setCode(message.getCode());
        msg.setMsg(message.getMessage());
        return msg;
    }
}
复制代码
配置默认值
  • 全局的axios默认值
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
复制代码
  • 自定义实例默认值
// 创建实例时设置配置的默认值
var instance = axios.create({
  baseURL: 'https://api.example.com'
});

// 在实例已创建后修改默认值
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
复制代码
  • 配置的优先顺序

配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者。这里是一个例子:

// 使用由库提供的配置的默认值来创建实例
// 此时超时配置的默认值是 `0`
var instance = axios.create();

// 覆写库的超时默认值
// 现在,在超时前,所有请求都会等待 2.5 秒
instance.defaults.timeout = 2500;

// 为已知需要花费很长时间的请求覆写超时设置
instance.get('/longRequest', {
  timeout: 5000
});
复制代码
拦截器

在请求或响应被 thencatch 处理前拦截它们。

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });

// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });
复制代码

如果你想在稍后移除拦截器,可以这样:

var myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
复制代码
取消

可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:

var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
复制代码
axios源码学习

找到axios下面的lib/axios.js文件

lib/axios.js中代码创建实例代码分析:

// lib/axios.js
function createInstance(defaultConfig) {
  // 创建一个Axios实例
  var context = new Axios(defaultConfig);

  // 以下代码也可以这样实现:var instance = Axios.prototype.request.bind(context);
  // 这样instance就指向了request方法,且上下文指向context,所以可以直接以 instance(option) 方式调用 
  // Axios.prototype.request 内对第一个参数的数据类型判断,使我们能够以 instance(url, option) 方式调用
  var instance = bind(Axios.prototype.request, context);

  // 把Axios.prototype上的方法扩展到instance对象上,
  // 这样 instance 就有了 get、post、put等方法
  // 并指定上下文为context,这样执行Axios原型链上的方法时,this会指向context
  utils.extend(instance, Axios.prototype, context);

  // 把context对象上的自身属性和方法扩展到instance上
  // 注:因为extend内部使用的forEach方法对对象做for in 遍历时,只遍历对象本身的属性,而不会遍历原型链上的属性
  // 这样,instance 就有了  defaults、interceptors 属性。(这两个属性后面我们会介绍)
  utils.extend(instance, context);

  return instance;
}

// 接收默认配置项作为参数(后面会介绍配置项),创建一个Axios实例,最终会被作为对象导出
var axios = createInstance(defaults);
复制代码
  • 拦截器 interceptors

    拦截器分为请求拦截器(interceptors.request)和响应拦截器(interceptors.response)。

    请求拦截器(interceptors.request)是指可以拦截住每次或指定http请求,并可修改配置项 ;

    响应拦截器(interceptors.response)是指http请求后拦截,并可修改返回结果项;

  • 数据转换器

    数据转换器分为请求转换器和响应转换器,顾名思义: 请求转换器(transformRequest)是指在请求前对数据进行转换, 响应转换器(transformResponse)主要对请求响应后的响应体做数据转换。

  • http请求适配器

  • config配置项

axios流程图

img

axios多种使用方式:
  • 第1种使用方式:axios(option)
axios({
  url,
  method,
  headers,
})
复制代码
  • 第2种使用方式:axios(url[, option])
axios(url, {
  method,
  headers,
})
复制代码
  • 第3种使用方式(对于get、delete等方法):axios[method](url[, option])
axios.get(url, {
  headers,
})
复制代码
  • 第4种使用方式(对于post、put等方法):axios[method](url[, data[, option]])
axios.post(url, data, {
  headers,
})
复制代码
  • 第5种使用方式:axios.request(option)
axios.request({
  url,
  method,
  headers,
})
复制代码
用户配置项对象config

http请求适配器、请求地址、请求方法、请求头header、 请求数据、请求或响应数据的转换、请求进度、http状态码验证规则、超时、取消请求等。可以发现,几乎axios所有的功能都是通过这个对象进行配置和传递的, 既是axios项目内部的沟通桥梁,也是用户跟axios进行沟通的桥梁。

import axios from 'axios'

// 第1种:直接修改Axios实例上defaults属性,主要用来设置通用配置
axios.defaults[configName] = value;

// 第2种:发起请求时最终会调用Axios.prototype.request方法,然后传入配置项,主要用来设置“个例”配置
axios({
  url,
  method,
  headers,
})

// 第3种:新建一个Axios实例,传入配置项,此处设置的是通用配置
let newAxiosInstance = axios.create({
  [configName]: value,
})

复制代码
跨域携带cookie
import axios from 'axios'

axios.defaults.withCredentials = true;
复制代码
在react项目中对axios简单封装使用

命名三个文件Api.js、ApiURL.js、ApiIp.js,其中ApiIp.js是用来获取服务器ip地址的,ApiURL.js是用来拼装ip地址和接口路径的,当然这个可以使用baseURL来实现也可以,即:axios.defaults.baseURL = 'api.example.com';

Api.js的实现

import axios from 'axios';
import {notification} from 'antd';
import * as ApiURL from 'src/HttpClientRequest/ApiURL';

const key = 'keepOnlyOne';

/**
 * 数据请求公共方法
 * @param apiURL      接口请求路径
 * @param configObj   用户传入配置的参数
 * @returns {Promise<any>}
 */

function getDataFromServer(apiURL, configObj) {

    // 获取用户传入的接口配置参数
    let {
        method = 'get',
        data = {},
        timeout = 3000,
        params = {}
    } = configObj;

    return new Promise(function (resolve, reject) {
        axios({
            url: apiURL,
            method: method,
            timeout: timeout,
            data: data,
            params: params,
            headers: {
                'Content-Type': 'application/json',
                'token': window.sessionStore.getItem('token') || ''
            }
        }).then((response) => {
            if (response) {
                if (response.data && response.data.code) {
                    resolve(response);
                } else {
                    notification.error({
                        key,
                        message: '操作失败',
                        description: '数据格式有误'
                    });
                    resolve(response);
                }
            } else {
                notification.error({
                    key,
                    message: '操作失败',
                    description: '服务器错误'
                });
                resolve(response);
            }
        }).catch((error) => {
            notification.error({
                key,
                message: '操作失败',
                description: '网络异常,请稍后重试'
            });
            reject(error);
        });
    });
}


export function login(configObj) {
    return getDataFromServer(ApiURL.LOGIN, configObj);
}
复制代码

ApiURL.js

import API_IP from './ApiIp';

// 登录接口地址
export const LOGIN = `${API_IP}/index/login`;
复制代码

ApiIp.js

// 获取当前浏览器的地址
const interfaceIp = window.location.host;
const imageAddrassIp = window.location.hostname;

// 获取当前浏览器的协议
const browserProtocol = window.location.protocol;

// 进行服务器请求地址的封装
const serverInterfaceIp = `${browserProtocol}//${interfaceIp}`;
const serverImageIp = `${browserProtocol}//${imageAddrassIp}`;

// 对外提供的请求地址(兼容生产环境和开发环境)
export const API_IP = process.env.NODE_ENV === 'development' ? 'http://121.0.0.1:8080' : serverInterfaceIp;
export const IMAGES_IP = process.env.NODE_ENV === 'development' ? 'http://121.0.0.1' : serverImageIp;
复制代码

参考文章:juejin.im/post/684490…