2.3 Request
Fetch APIRequest 的接口表示资源请求。
你可以使用
Request()构造函数创建一个新的Request对象,但你更有可能遇到一个Request对象作为另一个API操作的结果被返回,比如一个service workerFetchEvent.request。
2.3.1 构造函数
- Request():创建一个新
Request对象。
2.3.2 实例属性
- Request.body - 只读:正文内容的 ReadableStream。
- Request.bodyUsed - 只读:存储
true或false,以表示该主体是否已经在请求中被使用。 - Request.cache - 只读:包含请求的缓存模式(例如,
default,reload,no-cache)。 - Request.credentials - 只读:包含请求的凭据(例如
omit,same-origin,include,same-origin)。默认值为。 - Request.destination - 只读:返回描述请求目的地的字符串。这是一个字符串,表示所请求内容的类型。
- Request.headers - 只读:Headers包含请求的关联对象。
- Request.integrity - 只读:包含请求的子资源完整性值(例如,
sha256-BpfBw7ivV8q2jLiT13fxDYAe2tJllusRSZ273h2nFSE=)。 - Request.method - 只读:包含请求的方法(
GET,POST等) - Request.mode - 只读:包含请求的模式(例如
cors,no-cors,same-origin,navigate) - Request.redirect - 只读:包含如何处理重定向的模式。它可能是
follow、error或manual之一。 - Request.referrer - 只读:包含请求的引荐来源网址(例如,
client)。 - Request.referrerPolicy - 只读:包含请求的引用策略(例如,
no-referrer)。 - Request.signal - 只读:返回
AbortSignal与请求关联的 - Request.url - 只读:包含请求的 URL。
2.3.3 实例方法
- Request.arrayBuffer():ArrayBuffer 返回一个以请求主体的表示解析的
promise。 - Request.blob():Blob 返回一个以请求主体的表示解析的
promise。 - Request.clone():创建当前对象的副本
Request。 - Request.formData():FormData 返回一个以请求主体的表示解析的
promise。 - Request.json():返回一个
promise,该promise以将请求主体解析为JSON. - Request.text():返回使用请求正文的文本表示解析的
promise。
注意: 请求体函数只能运行一次;后续调用将解析为
空字符串/ArrayBuffers。
2.3.4 例子
在下面的代码片段中,我们使用构造函数创建一个新请求Request()(对于与脚本位于同一目录中的图像文件),然后返回请求的一些属性值:
const request = new Request('https://www.mozilla.org/favicon.ico'); const url = request.url; const method = request.method; const credentials = request.credentials;
Request然后,您可以通过将对象作为参数传递给调用来获取此请求fetch(),例如:
fetch(request) .then((response) => response.blob()) .then((blob) => { image.src = URL.createObjectURL(blob); });
在以下代码片段中,我们使用Request()构造函数创建一个新请求,其中包含一些初始数据和需要主体有效负载的 API 请求的主体内容:
const request = new Request('https://example.com', {method: 'POST', body: '{"foo": "bar"}'}); const url = request.url; const method = request.method; const credentials = request.credentials; const bodyUsed = request.bodyUsed;
注意: body 只能是 Blob、 ArrayBuffer、TypedArray、DataView、FormData、URLSearchParams、ReadableStream 或 String 对象,以及字符串文字,因此要将 JSON 对象添加到有效负载,你需要对该对象进行字符串化。
然后,你可以通过将 Request 对象作为参数传递给调用来获取此 API 请求fetch(),例如并获得响应:
fetch(request) .then((response) => { if (response.status === 200) { return response.json(); } else { throw new Error('Something went wrong on API server!'); } }) .then((response) => { console.debug(response); // … }).catch((error) => { console.error(error); });
2.4 Response
Fetch APIResponse 的接口表示对请求的响应。
你可以使用 Response() 构造函数创建一个新的
Response对象,但你更有可能遇到一个Response对象作为另一个API操作的结果被返回--例如,一个service workerFetchEvent.replyWith,或一个简单的 fetch()。
2.4.1 构造函数
- Response():创建一个新
Response对象。
2.4.2 实例属性
- Response.body:正文内容的 ReadableStream。
- Response.bodyUsed - 只读:存储一个布尔值,声明正文是否已在响应中使用。
- Response.headers - 只读:Headers 与响应关联的对象。
- Response.ok - 只读:一个布尔值,指示响应是否成功(状态在
200–范围内299)。 - Response.redirected - 只读:指示响应是否是重定向的结果(即,其 URL 列表具有多个条目)。
- Response.status - 只读:响应的状态代码。(这将是
200成功的)。 - Response.statusText - 只读:状态码对应的状态信息。(例如,
OK对于200)。 - Response.type - 只读:响应的类型(例如,
cors,basic)。 - Response.url - 只读:响应的 URL。
2.4.3 静态方法
- Response.error():
Response返回与网络错误关联的新对象。 - Response.redirect():使用不同的 URL 创建新的响应。
2.4.4 实例方法
Response.arrayBuffer():返回一个Promise,该Promise以 ArrayBuffer 响应主体的表示形式解析。- Response.blob():返回一个
Promise,该Promise以Blob响应主体的表示形式解析。 - Response.clone():创建对象的克隆
Response。 - Response.formData():返回一个
Promise,该Promise以FormData响应主体的表示形式解析。 - Response.json():返回一个
Promise,该Promise以将响应正文文本解析为JSON. - Response.text():返回一个
Promise,该Promise以响应主体的文本表示形式解析。
2.4.5 例子
在我们的 基本获取示例 中(实时运行示例),我们使用一个简单的fetch()调用来抓取一张图片并将其显示在<img>元素中。fetch()调用返回一个 promise,它解析为与资源获取操作相关的Response对象。
你会注意到,由于我们正在请求图像,因此我们需要运行 Response.blob 以向响应提供正确的 MIME 类型。
const image = document.querySelector(".my-image"); fetch("flowers.jpg") .then((response) => response.blob()) .then((blob) => { const objectURL = URL.createObjectURL(blob); image.src = objectURL; });
2.5 Fetch API 优缺点
Fetch API 是一个基于 Promise 设计的 JavaScript API,用于在 Web 应用程序中进行网络请求。以下是 Fetch API 的优缺点:
优点:
- 更简洁的代码:相比
XMLHttpRequest,Fetch API的语法更加简单易懂,使用起来更加方便。 - 基于
Promise设计:Promise是JavaScript中一种非常强大的异步编程机制,可以帮助开发者更好地处理异步操作,避免回调地狱。 - 支持跨域请求:
Fetch API内置了跨域请求的支持,可以让开发者更加轻松地处理跨域问题。 - 更好的错误处理:
Fetch API在网络请求失败时会抛出异常,可以帮助开发者更好地处理错误。 - 可以自定义请求头:
Fetch API允许开发者自定义请求头,可以更好地控制请求。
缺点:
- 不兼容旧版本浏览器:
Fetch API并不是所有浏览器都支持的,特别是在一些旧版本浏览器中可能会有兼容性问题。 - 默认不携带
cookie:Fetch API默认不携带cookie,需要手动设置才能携带cookie。 - 无法取消请求:
Fetch API并不支持直接取消请求,需要开发者自己处理取消请求的逻辑。
2.6 Fetch API 安全性问题
Fetch API 是用于获取和发送资源的现代 Web API。它可以使网络请求更加灵活和高效。然而,由于它的开放性和灵活性,Fetch API 也可能存在一些安全性问题。以下是一些可能存在的安全性问题:
- 跨站点请求伪造(
CSRF):攻击者可以通过伪造一个可信站点的请求,以在用户不知情的情况下执行某些操作。这可以通过在请求头中包含攻击者的自定义Cookie或其他授权信息来实现。 - 跨域资源共享(
CORS):由于浏览器的同源策略限制,浏览器只允许来自同一域的请求。但是,使用CORS,站点可以通过在响应头中包含Access-Control-Allow-Origin来授权其他站点进行请求。如果站点的CORS设置不正确,攻击者可能会利用它来执行跨域攻击。 HTTPS劫持:攻击者可能会尝试在HTTPS连接中中间攻击并篡改Fetch API请求。这可以通过使用代理或恶意软件来实现。- 注入攻击:如果站点的Fetch API处理不正确,攻击者可能会利用它来执行
SQL注入或其他类型的注入攻击。
为了避免这些安全问题,开发人员应该采取适当的措施来保护站点的Fetch API。例如:
- 使用随机生成的令牌来防止
CSRF攻击。 - 设置适当的CORS策略,并使用适当的
Access-Control-Allow-Origin标头来限制请求来源。 - 在
HTTPS连接中使用证书,并使用证书钩子来验证证书。 - 对于所有输入,使用参数化查询来避免注入攻击。
3、Axios
3.1 什么是 Axios?
官方:Axios 是一个基于 promise 的 JavaScript HTTP 客户端,可用于node.js和浏览器。它是同构的(=它可以在浏览器和nodejs中以相同的代码库运行)。在服务器端,它使用本地
node.js的http模块,而在客户端(浏览器)则使用XMLHttpRequests。Axios 支持
JS ES6原生的Promise API,它比.fetch()具有的另一个特性是它执行 JSON 数据的自动转换。
它的 Features:
- 从浏览器中发出 XMLHttpRequests
- 从
node.js发出http请求 - 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 超时处理
- 查询参数序列化,支持嵌套条目
- 自动将请求主体序列化为:
JSON (application/json)Multipart/FormData(multipart/form-data)URL编码的表单(application/x-www-form-urlencoded)
- 将HTML表单发布为
JSON - 响应中自动处理
JSON数据 - 为浏览器和
node.js捕捉进度,并提供额外的信息(速度、剩余时间)。 - 为
node.js设置带宽限制 - 与符合规范的
FormData和Blob(包括node.js)兼容 - 客户端支持对 XSRF 的保护
3.2 Axios 基本用法
3.2.1 axios 实例
可以用自定义配置创建一个新的 axios 实例:
const instance = axios.create({ baseURL: 'https://some-domain.com/api/', timeout: 1000, headers: {'X-Custom-Header': 'foobar'} });
下面是可用的一些实例方法,指定的配置将被合并到实例配置中:
axios#request(config)axios#get(url[, config])axios#delete(url[, config])axios#head(url[, config])axios#options(url[, config])axios#post(url[, data[, config]])axios#put(url[, data[, config]])axios#patch(url[, data[, config]])axios#getUri([config])
3.2.2 处理请求
axios 为所有支持的请求方法提供了简洁的 api:
axios.request(config)axios.get(url[, config])axios.delete(url[, config])axios.head(url[, config])axios.options(url[, config])axios.post(url[, data[, config]])axios.put(url[, data[, config]])axios.patch(url[, data[, config]])axios.postForm(url[, data[, config]])axios.putForm(url[, data[, config]])axios.patchForm(url[, data[, config]])
3.2.2.1 发送 GET 请求
axios.get('/user?ID=12345') .then(function (response) { // handle success console.log(response); }) .catch(function (error) { // handle error console.log(error); }) .finally(function () { // always executed }); // 也可以用以下方式 axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }) .finally(function () { // always executed }); async function getUser() { try { const response = await axios.get('/user?ID=12345'); console.log(response); } catch (error) { console.error(error); } }
还可以通过传递相关的配置参数实现get请求:
// GET request for remote image in node.js axios({ method: 'get', url: 'http://bit.ly/2mTM3nY', responseType: 'stream' }) .then(function (response) { response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')) });
多个并行请求:
function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } const [acct, perm] = await Promise.all([getUserAccount(), getUserPermissions()]); // or Promise.all([getUserAccount(), getUserPermissions()]) .then(function ([acct, perm]) { // ... });
3.2.2.2 发送 POST 请求
axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // or axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } });
- Application/json
const {data} = await axios.post('/user', document.querySelector('#my-form'), { headers: { 'Content-Type': 'application/json' } })
- Multipart (
multipart/form-data)
const {data} = await axios.post('https://httpbin.org/post', { firstName: 'Fred', lastName: 'Flintstone', orders: [1, 2, 3], photo: document.querySelector('#fileInput').files }, { headers: { 'Content-Type': 'multipart/form-data' } } )
- URL encoded form (
application/x-www-form-urlencoded)
const {data} = await axios.post('https://httpbin.org/post', { firstName: 'Fred', lastName: 'Flintstone', orders: [1, 2, 3] }, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } })
3.2.2.3 发送 PUT 请求
axios.put('/api/users/1', { name: 'John Doe', email: 'john@example.com' }) .then(response => console.log(response.data)) .catch(error => console.log(error));
3.2.2.4 发送 DELETE 请求
axios.delete('/api/users/1') .then(response => console.log(response.data)) .catch(error => console.log(error));
3.2.2.5 设置请求头
axios.get('/api/users', { headers: { 'Authorization': 'Bearer ' + token } }) .then(response => console.log(response.data)) .catch(error => console.log(error));
3.3 取消请求
timeout在 axios 调用中设置属性会处理与响应相关的超时。在某些情况下(例如网络连接变得不可用),axios 调用将受益于提前取消 连接。如果不取消,axios 调用可能会挂起,直到父代码/堆栈超时(在服务器端应用程序中可能是几分钟)。
要终止 axios 调用,你可以使用以下方法:
signalcancelToken(弃用)
组合 timeout 和取消方法(例如 signal)应涵盖与 响应 相关的超时和 连接 相关的超时。
3.3.1 signal: 中止控制器
从
v0.22.0 axios开始支持 AbortControllerfetch API方式取消请求:
const controller = new AbortController(); axios.get('/foo/bar', { signal: controller.signal }).then(function(response) { //... }); // cancel the request controller.abort()
AbortSignal.timeout() 使用最新 API [nodejs 17.3+] 的超时示例:
axios.get('/foo/bar', { signal: AbortSignal.timeout(5000) //Aborts request after 5 seconds }).then(function(response) { //... });
带有超时辅助函数的示例:
function newAbortSignal(timeoutMs) { const abortController = new AbortController(); setTimeout(() => abortController.abort(), timeoutMs || 0); return abortController.signal; } axios.get('/foo/bar', { signal: newAbortSignal(5000) //Aborts request after 5 seconds }).then(function(response) { //... });
3.3.2 CancelToken
你还可以使用 CancelToken 取消请求。
- axios cancel token API 基于撤回的可取消
promise提案。- 此 API 已弃用,因为
v0.22.0不应在新项目中使用
你可以使用工厂创建取消令牌,CancelToken.source如下所示:
const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // 处理错误 } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // 取消请求(消息参数是可选的) source.cancel('Operation canceled by the user.');
你还可以通过将执行函数传递给构造函数来创建取消令牌CancelToken:
const CancelToken = axios.CancelToken; let cancel; axios.get('/user/12345', { cancelToken: new CancelToken(function executor(c) { // 一个执行器函数接收一个取消函数作为参数 cancel = c; }) }); // cancel the request cancel();
在过渡期间,你可以使用两种取消 API,即使是针对同一个请求:
const controller = new AbortController(); const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user/12345', { cancelToken: source.token, signal: controller.signal }).catch(function (thrown) { if (axios.isCancel(thrown)) { console.log('Request canceled', thrown.message); } else { // handle error } }); axios.post('/user/12345', { name: 'new name' }, { cancelToken: source.token }) // cancel the request (the message parameter is optional) source.cancel('Operation canceled by the user.'); // OR controller.abort(); // the message parameter is not supported
3.4 拦截请求和响应
axios.interceptors.request.use(config => { // Do something before request is sent return config; }, error => { // Do something with request error return Promise.reject(error); }); axios.interceptors.response.use(response => { // Do something with response data return response; }, error => { // Do something with response error return Promise.reject(error); });
3.5 错误处理
axios.get('/user/12345') .catch(function (error) { if (error.response) { // 请求已经发出,但是服务器回应的状态码不是 2xx。 console.log(error.response.data); console.log(error.response.status); console.log(error.response.headers); } else if (error.request) { // 请求已经发出,但没有收到响应 // `error.request`在浏览器中是 XMLHttpRequest 的一个实例 // 在node.js中是 http.ClientRequest 的一个实例。 console.log(error.request); } else { // 设置触发错误的请求时需要处理的一些事情 console.log('Error', error.message); } console.log(error.config);
3.6 Axios 优缺点
优点:
Promise API:Axios使用Promise API,因此可以轻松处理异步操作。Promise API具有更清晰的语法和更好的可读性,因为它们允许在异步操作完成之前进行链式调用,从而避免了回调地狱问题。- 简单易用:
Axios的API设计简单且易于使用,而且它还提供了许多可用的配置选项,例如设置请求头、超时时间、认证等等,让开发者可以更轻松地定制请求。 - 可扩展性:
Axios可以通过添加拦截器(interceptors)来实现许多自定义功能,例如添加请求拦截器、响应拦截器和错误拦截器等等。这些拦截器可以让开发者在请求和响应过程中进行自定义操作。 - 支持浏览器和
Node.js:Axios可以同时在浏览器和Node.js环境中使用。这意味着开发者可以使用相同的代码库在两个不同的环境中实现网络请求功能。 - 轻量级:
Axios是一个轻量级库,它只依赖于Promise和一些基本的JavaScript库,因此它的体积相对较小。
缺点:
- 学习曲线:尽管
Axios的API设计简单且易于使用,但是它仍然需要一些学习和理解。特别是对于初学者来说,可能需要一些时间才能掌握如何使用Axios。 - 对于大型应用程序可能不够强大:对于大型应用程序来说,
Axios可能会显得有些简单。因为它不提供复杂的数据管理功能,例如状态管理、数据缓存等等。这时候可能需要使用其他更为强大的HTTP客户端库(比如TanStack Query)来实现这些功能。 - 可能出现跨域问题:
Axios不能直接解决跨域请求的问题。尽管Axios可以设置跨域请求头,但是它不能绕过浏览器的安全限制。这意味着在某些情况下,开发者可能需要通过其他方式来解决跨域请求的问题。
3.7 Axios 安全性
Axios 在 Web 安全方面做了以下工作:
- 防止跨站点请求伪造(
CSRF)攻击:Axios 会自动在请求头中添加CSRF token(如果可用的话),以防止攻击者利用用户身份进行 CSRF 攻击。此外,Axios还支持自定义请求头,允许开发人员手动添加其他安全相关的请求头。 - 防止
XSS攻击:Axios不会自动解析或执行响应的内容,因此可以防止跨站点脚本(XSS)攻击。 - 支持
HTTPS:Axios支持使用HTTPS进行加密传输,从而保护数据的安全性。 - 支持自定义验证:
Axios提供了自定义验证的接口,允许开发人员根据自己的需求进行验证,以确保请求和响应的安全性。 - 支持取消请求:
Axios提供了取消请求的接口,允许开发人员在请求过程中取消请求,以避免恶意攻击或其他安全问题的发生。
4、Fetch API 和 Axios 的区别
Ajax 实际上不是一个特定的技术,而是一系列技术的统称,通常我们将其默认为是 XMLHttpRequest。Fetch API 是ES6之后出现的一个新的基于 Promise 的新API,可以认为是 XMLHttpRequest 的最新替代品,开发人员终于可以忘记丑陋且难以使用的HTMLHttpRequests了。Axios 则是一个专门用于HTTP请求的库,也是基于 Promise。实际开发中我们可能更多使用 Fetch API 和 Axios,接下来就结合实际例子来分析它们的区别。
4.1 基本定义
4.1.1 Axios
Axios 是用于发出网络请求的第三方 HTTP 客户端库。它是基于 Promise 的,可以在以下示例中看到:
axios.get('https://fakestoreapi.com/products/1') .then(response => console.log(response));
默认情况下,响应数据以 JSON 格式提供,因此我们可以使用 data 属性立即访问它。它在底层使用XMLHttpRequest,这是它广泛支持浏览器的原因之一。你可以通过 CDN 或使用包管理器(如npm)将其添加到你的项目中。
核心功能包括:
- 在浏览器中创建 XMLHttpRequests
- 在 node.js 环境中发出 http 请求
- 取消请求
- 拦截请求和响应
兼容性方面,大多数浏览器都广泛支持 Axios,即使是像 IE11 这样的旧浏览器。
4.1.2 Fetch
Fetch 是一个基于 promise 的 API。看一个简单的例子:
fetch('https://fakestoreapi.com/products/1') .then((response) => console.log(response.json())) .catch((error) => console.error(error));
Fetch API 提供了一个简单易用的接口来获取资源,更具体地说,它为我们提供了访问和操作部分 HTTP 管道的方法,其中部分是请求和响应。
Fetch API 提供了fetch() 方法。使用 Fetch API 我们可以完全实现 Axios 的所有核心功能。实际上,Fetch API 是一个比 Axios 具有更多可能性的原生接口。但是,因为它是底层的API,所以通常使用起来有点繁琐。
兼容性方面,Fetch 是一个内置的 API,因此我们不需要安装或导入任何东西。它在所有现代浏览器中都可用,您可以在caniuse上查看。Fetch 在 node.js 中也可用 - 可以在此处阅读更多相关信息。
请记住,即使你的浏览器不支持 Fetch,你始终可以使用polyfill。然而,如果你必须使用这个 polyfill,你可能还需要一个promise polyfill。
4.2 基本语法
4.2.1 Axios
Axios 提供了多种不同的调用方式:
axios(url, { // configuration options })
我们可以使用点表示法,而不是在配置选项中指定 HTTP 方法:
axios.post(url, { // configuration options })
如果我们省略配置选项和点符号,它默认为 GET 方法:
axios(url)
我们可以为请求使用许多不同的配置设置,使用第二个参数:
axios(url, { method: 'post', timeout: 1000, headers: { "Content-Type": "application/json", }, data: { property: "value", }, })
4.2.2 Fetch
Fetch 接收两个参数,第一个参数是 URL,第二个是配置选项:
fetch(url, { method: 'POST', headers: { "Content-Type": "application/json", }, body: JSON.stringify({ property: "value", }), })
有关配置选项的完整列表,请查看此链接。
4.2.3 总结
来看两个具体的例子:
let url = 'https://someurl.com'; let options = { method: 'POST', mode: 'cors', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json;charset=UTF-8' }, body: JSON.stringify({ property_one: value_one, property_two: value_two }) }; let response = await fetch(url, options); let responseOK = response && response.ok; if (responseOK) { let data = await response.json(); // do something with data }
let url = 'https://someurl.com'; let options = { method: 'POST', url: url, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json;charset=UTF-8' }, data: { property_one: value_one, property_two: value_two } }; let response = await axios(options); let responseOK = response && response.status === 200 && response.statusText === 'OK'; if (responseOK) { let data = await response.data; // do something with data }
基本语法非常相似,几乎完全相同。但是,存在多个小差异:
- 不同的属性用于 post 请求以将数据发送到
endpoint-Axios使用data属性,而fetch使用body属性。 - 我们需要将数据序列化为
JSON字符串来发送数据。Axios在使用POST方法向API发送JavaScript对象时自动将数据字符串化。 Axios会自动转换从服务器返回的数据,但使用fetch()时,你必须调用response.json方法,将数据解析为JavaScript对象。关于response.json方法的更多信息可以在这里找到。- 使用
fetch()时,我们必须使用JSON.stringify将数据转换为字符串。 - 使用
Axios,服务器提供的数据响应可以在 数据对象 中访问,而对于fetch()方法,最终数据可以被命名为任何变量。