导航


HTML

CSS

JavaScript

浏览器 & 网络

版本管理

框架

构建工具

TypeScript

性能优化

软实力

算法

UI、组件库

Node

冷门技能

Aios

request config

{
  url: '/user',
  method: 'get', // default
  baseURL: '<https://some-domain.com/api>',

  // 'transformRequest' 允许在请求数据被发送到服务器之前对其进行更改
  // 这只适用于请求方法 'PUT'、'POST'、'PATCH' 和 'DELETE'
  // 必须 return 一个 string 或者 Buffer, ArrayBuffer, FormData or Stream 的实例
  // 你可以修改header对象
  transformRequest: [function (data, headers) {
    // 执行想要转换数据的任何操作
    return data;
  }],

  // 'transformResponse' 在传递给 then/catch 前,允许修改响应数据
  transformResponse: [function (data) {
    // 执行想要转换数据的任何操作
    return data;
  }],

  // 'headers' 是要发送的自定义头信息
  headers: {'X-Requested-With': 'XMLHttpRequest'},

  // 'params' 是与请求一起发送的URL参数
  // 必须是 普通对象 或 URLSearchParams 对象
  // 注意: 如果参数为 null 或 undefined ,则不会在 URL 中呈现
  params: {
    ID: 12345
  },

  // 'paramsSerializer' 是一个负责序列化 'params' 的可选函数
  paramsSerializer: function (params) {
    return Qs.stringify(params, {arrayFormat: 'brackets'})
  },

  // 'data' 是作为请求体发送的数据
  // 仅适用于请求方法 'PUT'、'POST'、'DELETE' 和 'PATCH'
  // 当没有设置 'transformRequest' 时,必须是以下类型之一:
  // - string, 普通对象, ArrayBuffer, ArrayBufferView, URLSearchParams
  // - 浏览器只支持: FormData, File, Blob
  // - Node 只支持: Stream, Buffer
  data: {
    firstName: 'Fred'
  },
  // 'timeout' 指定请求超时前的毫秒数
  // 如果请求时间超过 'timeout',则请求将被中止
  timeout: 1000, // 默认值是 `0`

  // `withCredentials` 当前请求为跨域类型时是否在请求中协带cookie
  withCredentials: false, // 默认

  // `responseType` indicates the type of data that the server will respond with
  // options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
  //   browser only: 'blob'
  responseType: 'json', // default

  // 'validateStatus' 定义是否 resolve 或 reject 给定 HTTP 响应状态码的 Promise
  // 如果此方法返回 `true` (或者设置成 `null/undefined`), 则 Promise 判定为 resolve;
  // 否则 Promise 会被 reject.
  validateStatus: function (status) {
    return status >= 200 && status < 300; // 默认 200、300 间才走 then,否则 catch
  },

  // 'cancelToken' 用来取消 ajax 请求
  cancelToken: new CancelToken(function (cancel) {
  }),
}

get 请求

async getStudents() {
  const data = await axios({
    url: '<http://localhost:3000/students>',
    method: 'get',
    params: {
      id: 3
    },
  });
  console.log(data);
}

Untitled

打印 data 后我们会发现,真正的数据在 data 下的 data 中 :

Untitled

修改 Content-Type

通过常规三种方式设置 Content-Type 都无效:

// 全局:
axios.defaults.headers.get['Content-Type'] = 'application/x-www-form-urlencoded';
// 实例中:
async getStudents() {
  const data = await axios({
    url: '<http://localhost:3000/students>',
    method: 'get',
    params: {
      id: 3
    },
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });
  console.log(data);
}
// 拦截器中:
axios.interceptors.request.use((config) => {
  config.headers.get['Content-Type'] = 'application/x-www-form-urlencoded';
  return config;
})

最后在查看 axios 源码时发现在没有设置 requestData (也就是 config.data)并且设置 content-type 时,会把 content-type 删除:

// Add headers to the request
if ('setRequestHeader' in request) {
  utils.forEach(requestHeaders, function setRequestHeader(val, key) {
    if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
      // Remove Content-Type if data is undefined
      delete requestHeaders[key];
    } else {
      // Otherwise add header to the request
      request.setRequestHeader(key, val);
    }
  });
}

这么做的原因在于是 get 请求本身是不需要 Content-Type的(get 请求在 不自定义headers 时是简单请求),但在知道源码的情况下,我们可以通过手动设置 data 来不走它此处的 if :

axios.interceptors.request.use((config) => {
  if (config.method === 'get') {
    config.data = true;
    config.headers.get['Content-Type'] = 'application/x-www-form-urlencoded';
  }

  console.log(config.headers);
  return config;
})

或者

async getStudents() {
  const data = await axios({
    url: '<http://localhost:3000/students>',
    method: 'get',
    params: {
      id: 3
    },
    data: true,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });
}

这样就能成功设置上了:

Untitled

get 在设置自定义 headers 后变成复杂请求

async getStudents() {
  const data = await axios({
    url: '<http://localhost:3000/students>',
    method: 'get',
    params: {
      id: 3
    },
    headers: {
      'X-Lance': 'Lance',
    }
  });
  console.log(data);
}

Untitled

Untitled

post 请求

让后端具备 post 能力:

const bodyParser = require('body-parser');

const app = express();

// 为 true 时将使用 qs 库处理数据,通常不需要
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

URLSearchParams(有兼容性问题 IE全系不支持)

const params = new URLSearchParams();
params.append("id", 1);
params.append("test", 'Lance');
const data = await axios({
  url: '<http://localhost:3030/teacher>',
  method: 'post',
  data: params,
});

application/x-www-form-urlencoded 配合 qs.stringify

const data = await axios({
  url: '<http://localhost:3000/teacher>',
  method: 'post',
  data: qs.stringify({
    id: 1,
    test: 'Lance'
  }),
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
});

或者使用 axios.post

const data = await axios.post(
  '<http://localhost:3000/teacher>',
  qs.stringify({ id: 1, test: 'Lance' }),
  {
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  }
)

Untitled

请求参数是字符串形式:

Untitled

Untitled

application/json,需要后端配合

const data = await axios({
  url: '<http://localhost:3000/teacher>',
  method: 'post',
  data: { id: 1 },
});
console.log(data);

Untitled

后端设置 Access-Control-Allow-Headers:

app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-methods', 'GET,POST');
  res.header('Access-Control-Allow-Headers', 'Origin,X-Requested-With,Content-Type,Accept');
  next();
})

前端请求:

Untitled

参数:

Untitled

Untitled

multipart/form-data 配合 new FormData 上传图片

前端代码:

<input
type="file" name="img" id="img"
@change="changeFile"
accept="image/*">上传图片
async changeFile(e) {
  console.log(e.target.files);
  const file = e.target.files[0];
  let formData = new FormData();
  formData.append("avatar", file);
  const data = await axios({
    url: '<http://localhost:3030/uploadImg>',
    method: 'post',
    data: formData
  })
  console.log(data);
}

Untitled

Untitled

Untitled

后端代码:

需要安装 multer 包帮助我们实现文件上传

const express = require('express');
const { resolve } = require('path');
const { readFileSync } = require('fs');
const bodyParser = require('body-parser');
const multer = require('multer');

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// 通过 filename 属性定制
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
      cb(null, './upload/');    // 保存的路径,备注:需要自己创建
  },
  filename: function (req, file, cb) {
      // 将保存文件名设置为 字段名 + 时间戳,比如 logo-1478521468943
      cb(null, file.originalname + '-' + Date.now());
  }
});
const upload = multer({ storage })

...

app.post('/uploadImg', upload.single('avatar'), (req, res) => {
  res.send({
    code: 1,
    msg: '上传成功'
  })
})

app.listen('3030', () => {
  console.log('start 3030');
});

Untitled

完整代码

前端

<template>
  <div id="app">
    <input
      type="file" name="img" id="img"
      @change="changeFile"
      accept="image/*">上传图片
  </div>
</template>

<script>
import qs from 'qs'
export default {
  name: 'App',
  mounted() {
    axios.interceptors.request.use((config) => {
      // 为了让 get 请求也能设置 content-type
      // if (config.method === 'get') {
      //   config.data = true
      //   config.headers.get['Content-Type'] = 'application/x-www-form-urlencoded';
      // }
      // if (config.method === 'post') {
      //   config.headers.post['Content-Type'] = 'application/json';
      // }
      console.log(config.headers);
      return config;
    })
    // this.getStudents();
    this.getTeacherById(1);
  },
  methods: {
    async getStudents() {
      const data = await axios({
        url: '<http://localhost:3030/students>',
        method: 'get',
        params: {
          id: 3
        },
        headers: {
          // 'X-Lance': 'Lance',
          // 'Content-Type': 'application/x-www-form-urlencoded'
        }
      });
      console.log(data);
    },
    async getTeacherById(id) {
      const params = new URLSearchParams();
      params.append("id", 1);
      params.append("test", 'Lance');
      const data = await axios({
        url: '<http://localhost:3030/teacher>',
        method: 'post',
        data: params,
      });
      // const data = await axios({
      //   url: '<http://localhost:3030/teacher>',
      //   method: 'post',
      //   data: qs.stringify({
      //     id
      //   }),
      //   headers: {
      //     'Content-Type': 'application/x-www-form-urlencoded'
      //   }
      // });
      // const data = await axios.post(
      //   '<http://localhost:3030/teacher>',
      //   qs.stringify({ id, test: 'Lance' }),
      //   {
      //     headers: {
      //       'Content-Type': 'application/x-www-form-urlencoded'
      //     }
      //   }
      // )
      // const data = await axios({
      //   url: '<http://localhost:3030/teacher>',
      //   method: 'post',
      //   data: { id, test: 'Lance' },
      // });
      console.log(data);
    },
    async changeFile(e) {
      console.log(e.target.files);
      const file = e.target.files[0];
      let formData = new FormData();
      formData.append("avatar", file);
      const data = await axios({
        url: '<http://localhost:3030/uploadImg>',
        method: 'post',
        data: formData
      })
      console.log(data);
    }
  }
}
</script>

<style lang="scss">

</style>

后端

const express = require('express');
const { resolve } = require('path');
const { readFileSync } = require('fs');
const bodyParser = require('body-parser');
const multer = require('multer');

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

// const upload = multer({ dest: 'upload/' });
// 通过 filename 属性定制
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
      cb(null, './upload/');    // 保存的路径,备注:需要自己创建
  },
  filename: function (req, file, cb) {
      // 将保存文件名设置为 字段名 + 时间戳,比如 logo-1478521468943
      cb(null, file.originalname + '-' + Date.now());
  }
});
const upload = multer({ storage })

app.all('*', (req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-methods', 'GET,POST');
  res.header('Access-Control-Allow-Headers', 'Origin,X-Requested-With,Content-Type,Accept');
  next();
})

app.get('/students', (req, res) => {
  const data = readFileSync(resolve(__dirname, './data/students.json'), 'utf8');
  res.send(JSON.parse(data));
})

app.post('/teacher', (req, res) => {
  const data = JSON.parse(readFileSync(resolve(__dirname, './data/teacher.json'), 'utf8'));
  const { id } = req.body;
  res.send(data.find(v => v.id == id) || {
    code: 0,
    msg: '未找到'
  });
})

app.post('/uploadImg', upload.single('avatar'), (req, res) => {
  res.send({
    code: 1,
    msg: '上传成功'
  })
})

app.listen('3030', () => {
  console.log('start 3030');
});

参考

https://www.cnblogs.com/dreamcc/p/10752604.html