HTTP 请求是任何与后端服务器通信的 Web 应用程序的重要组成部分。 前端需要一些数据,因此它通过网络 HTTP 请求(或 Ajax,因为它通常被称为)请求数据,服务器返回一个答案。 如今,几乎每个网站都以某种方式执行此操作。
对于一个较大的网站,我们可以预期会看到更多这样的操作。 更多数据、更多 API 和更多特殊情况。 当网站像这样增长时,保持组织性很重要。 一个经典的概念是 DRY(Don’t Repeat Yourself 的缩写),它指的是将代码抽象化以防止重复使用的过程。 这是理想的,因为它通常允许我们只编写一次代码,在多个地方使用它,并在一个地方更新,而不是在每个实例中更新。
我们可能还会使用库来帮助我们。 对于 Ajax,axios 是一个流行的选择。 您可能已经熟悉它,甚至在开发过程中使用它来执行独立的 POST 和 GET 请求。
安装和基本知识
可以使用 npm(或 yarn)安装它
npm install axios
使用 Axios 的独立 POST 请求如下所示
axios.post('https://axios-app.firebaseio.com/users.json', formData)
.then(res => console.log(res))
.catch(error => console.log(error))
原生 JavaScript 也有多种方法来执行 JavaScript。 值得注意的是,fetch()
。 那么为什么要使用库呢? 首先,fetch 中的错误处理非常奇怪。 使用 axios,您将从一开始就拥有更好的体验。 如果您想看到比较,我们有一篇文章涵盖了这两种方法,还有一篇文章讨论了这种抽象的价值。
选择 axios 的另一个原因是什么? 它为我们提供了更多保持 DRY 的机会,让我们深入了解一下。
全局配置
我们可以设置一个全局配置(例如在我们的 main.js
文件中),使用标准配置来处理所有应用程序请求,该配置通过 axios 附带的默认对象设置。
此对象包含
baseURL:
一个相对 URL,充当所有请求的前缀,每个请求都可以附加 URLheaders
: 可以根据请求设置的自定义标头timeout:
请求中止的点,通常以毫秒为单位测量。 默认值为0
,表示不适用。withCredentials
: 指示是否应使用凭据进行跨站点访问控制请求。 默认值为false
。responseType
: 指示服务器将返回的数据类型,选项包括json
(默认)、arraybuffe
r、document
、text
和stream
。responseEncoding
: 指示用于解码响应的编码。 默认值为utf8
。xsrfCookieName
: 用作 XSRF 令牌值的 cookie 的名称,默认值为XSRF-TOKEN
。xsrfHeaderName
: 传递 XSRF 令牌值的 HTTP 标头的名称。 默认值为X-XSRF-TOKEN
。maxContentLength
: 定义允许的 HTTP 响应内容最大大小(以字节为单位)maxBodyLength
: 定义允许的 HTTP 请求内容最大大小(以字节为单位)
大多数情况下,您只会使用 baseURL
、header
,以及可能 timeout
。 其余的不太常用,因为它们具有智能默认值,但知道它们的存在以备不时之需还是不错的。
这就是 DRY 的作用。 对于每个请求,我们不必重复 API 的 baseURL
,也不必重复我们可能需要在每个请求中使用的重要标头。
以下是一个示例,其中我们的 API 具有一个基础,但它还具有多个不同的端点。 首先,我们设置一些默认值
// main.js
import axios from 'axios';
axios.defaults.baseURL = 'https://axios-app.firebaseio.com' // the prefix of the URL
axios.defaults.headers.get['Accept'] = 'application/json' // default header for all get request
axios.defaults.headers.post['Accept'] = 'application/json' // default header for all POST request
Then, in a component, we can use axios more succinctly, not needing to set those headers, but still having an opportunity to customize the final URL endpoint:
// form.js component
import axios from 'axios';
export default {
methods : {
onSubmit () {
// The URL is now https://axios-app.firebaseio.com/users.json
axios.post('/users.json', formData)
.then(res => console.log(res))
.catch(error => console.log(error))
}
}
}
注意: 此示例在 Vue 中,但此概念可扩展到任何 JavaScript 环境。
自定义实例
设置“自定义实例”类似于全局配置,但作用域限定为指定组件。 因此,它仍然是一种 DRY 技术,但具有层次结构。
我们将在一个新文件中设置自定义实例(我们称之为 authAxios.js
),并将它导入到“关注”组件中。
// authAxios.js
import axios from 'axios'
const customInstance = axios.create ({
baseURL : 'https://axios-app.firebaseio.com'
})
customInstance.defaults.headers.post['Accept'] = 'application/json'
// Or like this...
const customInstance = axios.create ({
baseURL : 'https://axios-app.firebaseio.com',
headers: {'Accept': 'application/json'}
})
然后,我们将此文件导入到表单组件中
// form.js component
// import from our custom instance
import axios from './authAxios'
export default {
methods : {
onSubmit () {
axios.post('/users.json', formData)
.then(res => console.log(res))
.catch(error => console.log(error))
}
}
}
拦截器
拦截器适用于全局配置或自定义实例可能过于通用的情况,因为如果您在其对象中设置了一个标头,它将应用于受影响组件中每个请求的标头。 拦截器能够动态更改任何对象属性。 例如,我们可以根据我们选择在拦截器中使用的任何条件发送不同的标头(即使我们在对象中已经设置了一个)。
拦截器可以位于 main.js
文件或自定义实例文件中。 请求在发送出去后会被拦截,并允许我们更改响应的处理方式。
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent, like we're inserting a timeout for only requests with a particular baseURL
if (config.baseURL === 'https://axios-app.firebaseio.com/users.json') {
config.timeout = 4000
} else {
return config
}
console.log (config)
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 like console.log, change header, or as we did here just added a conditional behaviour, to change the route or pop up an alert box, based on the reponse status
if (response.status === 200 || response.status 201) {
router.replace('homepage') }
else {
alert('Unusual behaviour')
}
console.log(response)
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
拦截器顾名思义,会拦截请求和响应,并根据提供的任何条件采取不同的行动。 例如,在上面的请求拦截器中,我们仅当请求具有特定的 baseURL
时才插入了一个条件超时。 对于响应,我们可以拦截它并修改我们收到的内容,例如更改路由或显示一个警报框,具体取决于状态码。 我们甚至可以根据不同的错误代码提供多个条件。
当您的项目变得越来越大,并且您开始拥有大量路由和嵌套路由,它们都根据不同的触发器与服务器通信时,拦截器将非常有用。 除了我在上面设置的条件之外,还有许多其他情况需要使用拦截器,具体取决于您的项目。
有趣的是,我们可以弹出拦截器以防止它产生任何影响。 我们需要将拦截器分配给一个变量,并使用恰如其分地命名的 eject
方法弹出它。
const reqInterceptor = axios.interceptors.request.use(function (config) {
// Do something before request is sent, like we're inserting a timeout for only requests with a particular baseURL
if (config.baseURL === 'https://axios-app.firebaseio.com/users.json') {
config.timeout = 4000
} else {
return config
}
console.log (config)
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
const resInterceptor = axios.interceptors.response.use(function (response) {
// Do something with response data like console.log, change header, or as we did here just added a conditional behaviour, to change the route or pop up an alert box, based on the reponse status
if (response.status === 200 || response.status 201) {
router.replace('homepage')
} else {
alert('Unusual behaviour')
}
console.log(response)
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
axios.interceptors.request.eject(reqInterceptor);
axios.interceptors.request.eject(resInterceptor);
虽然不太常用,但可以将拦截器放到条件语句中,或者根据某些事件删除一个拦截器。
希望这能让你很好地了解 axios 的工作原理,以及它如何在应用程序中用于保持 API 请求 DRY。 虽然我们只是简单地介绍了一些常见用例和配置,但 axios 还有很多其他优势,您可以在文档中探索,包括取消请求和防止跨站点请求伪造等功能,以及其他令人惊叹的功能。
json 的正确 mime 类型是
application/json
,而不是像您这里一样使用文件扩展名.json
。已记录。 谢谢!
当存在现代的基于标准的
fetch
API 并且内置于浏览器中时,为什么还要添加另一个依赖项(这会增加维护成本,并且我们最近发现一些库,如request
已经过时并且被弃用)?来自帖子
@Geoff Graham(似乎无法直接回复)我从未真正理解这一点
误解一个功能并不意味着该功能“有问题”。这意味着你的理解有问题。我希望更多的人能区分这一点。我假设这里所说的“有问题”指的是 fetch 将 404s 等视为正结果而不是错误。
除此之外,我认为 axios 中没有什么你用 fetch 做不到的。 (除了 axios 在节点中的工作方式,我对此没有经验。还有可能请求状态 IIRC。)
我同意 Adam 的观点。
另外,我找不到任何引用的文章。
嘿,Martin!我并没有站在哪一边。原始评论提出了一个问题,作者在“安装和基础知识” 部分回答了这个问题。很高兴听到你对此有自己的看法。
有一个
application.json
而不是application/json
。这是一个拼写错误。它已经更正了 :)
我知道全局配置和拦截器。我在 React 应用程序中使用带有令牌的拦截器,但我遇到过根据架构发送不同令牌的情况。现在我知道我可以弹出或(在需要不同 baseurl 的情况下)创建一个实例。
好文章。谢谢。
好文章!只是想提醒一下阅读本文的读者,请考虑 axios 是否应该是项目的默认选择(我见过很多团队首先选择它,而不是考虑他们的实际需求)。
如果你需要 IE 支持,那么是的,axios 可能是你最好的选择。
但是,如果你需要比 fetch API 更好的可用性,我建议使用
fetch
的自定义包装器,或使用 wretch 等小型库。相比之下,axios 相当重。axios 类服务使用多个 url 服务器怎么样
可以使用类似的东西:https://npmjs.net.cn/package/axios-multi-api 在使用 Axios 时保持 100% DRY。
免责声明:我是 npm 包的作者,正好出现在你的博客上,所以就分享到这里了 :)