前言
在当今的 Web 开发中,前端与后端的数据交互是构建动态应用的核心。API 是连接不同软件应用的重要桥梁,允许开发者通过 HTTP 请求与服务器交互,高效调用API数据对于构建现代 Web 应用至关重要。传统的页面刷新方式已经无法满足用户对流畅体验的需求,而 Fetch API 的出现为 JavaScript 带来了全新的生命力。
一、Fetch API 概述
1.1 Fetch API 是什么❓
Fetch API 是现代浏览器提供的一个用于发起网络请求的接口,用于发起 HTTP 请求。它提供简洁的异步API,使开发者能够以更现代的方式与服务器交互。它是传统的 XMLHttpRequest 的替代品,提供了更简洁、更强大的功能。基于 Promise 实现,使异步操作更加直观。
相比于传统的 XMLHttpRequest 更加强大、灵活且易于使用。Fetch 基于 Promise 设计,使得异步请求的处理更加优雅。
| 特性 | Fetch API | XMLHttpRequest |
|---|---|---|
| 语法 | 基于 Promise,更简洁 | 回调函数,较复杂 |
| 请求/响应对象 | 标准化 | 非标准化 |
| 默认携带 Cookie | 不携带 | 携带 |
| 超时控制 | 需要额外实现 | 原生支持 |
| 取消请求 | 使用 AbortController | 原生支持 |
| 进度事件 | 有限支持 | 完整支持 |
1.2 Fetch 的基本语法
Fetch API 的基本用法是通过调用 fetch() 函数并传入一个 URL 作为参数来发起网络请求。该函数返回一个Promise对象,可以在其then()方法中处理请求成功的情况,在catch()方法中处理请求失败的情况。Fetch API 最基本的形式如下所示。
1fetch(url, options) 2 .then(response => response.json()) // 解析 JSON 数据 3 .then(data => console.log(data)) // 处理数据 4 .catch(error => console.error('出现错误:', error)); // 错误处理 5
上述代码示例展示了使用 Fetch API 发起一个请求,返回的 Promise 解析为响应对象,进而能访问响应体数据。处理响应体通常包含 JSON 数据,通过 .json() 方法解析。如果请求失败,fetch 返回的 promise 会拒绝,并将错误信息传给 catch 方法。
1.3 fetch 配置选项
fetch 接受第二个可选参数,一个可以控制不同配置的对象,常见属性如下表所示。
| 配置项 | 简要描述 | 常用值 |
|---|---|---|
| method | 请求的 HTTP 方法,默认方法为GET | GET、POST、PUT、PATCH、DELETE |
| headers | 请求中 HTTP 标头 | |
| body | 请求体。请注意,使用 GET 和 HEAD 方法的请求不能有正文 | |
| mode | 指定请求的模式。 | cors:默认值,允许跨域请求same-origin:只允许同源请求。no-cors:不能添加跨域的复杂标头,相当于提交表单所能发出的请求 |
| credentials | 指定是否发送 Cookie | same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送include:不管同源请求,还是跨域请求,一律发送 Cookieomit:一律不发送 |
| cache | 指定如何处理缓存 | default:默认值,先在缓存里面寻找匹配的请求no-store:直接请求远程服务器,并且不更新缓存reload:直接请求远程服务器,并且更新缓存no-cache:force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误 |
| redirect | 如何处理 HTTP 重定向响应,默认设置为follow | follow、error、manual |
| referrer | 包含请求的反向链接的字符串,默认为空字符串 | |
| referrerPolicy | 指定用于请求的反向链接政策 | |
| signal | AbortSignal 对象实例,支持接口中止请求 | |
| priority | 指定当前请求相对于其他同类请求的优先级,默认设置为auto | high、low、auto |
二、Fetch API 的基本使用
Fetch API 支持多种 HTTP 请求方法,如GET、POST、PUT、DELETE等。默认情况下,fetch() 函数会发送 GET 请求。如果需要发送其他类型的请求,可以在fetch() 函数的第二个参数中指定请求的配置对象。
2.1 发起 GET 请求
GET 请求是最常见的请求类型,用于从服务器获取数据。在Fetch API中,构造一个 GET 请求的URL是一件非常简单的事情。首先需要了解的是,GET请求的参数通常是通过URL的查询字符串(query string)部分传递给服务器的。
1fetch('https://api.example.com/data') 2 .then(response => response.json()) 3 .then(data => console.log(data)) 4 .catch(error => console.error('出现错误:', error)); 5
在上述例子中,fetch 执行 GET 请求,在构建URL时,需要确保查询参数是经过URL编码的,以避免查询字符串解析错误。一旦发起 GET 请求,就需要处理服务器返回的响应数据,Fetch API 返回的 response 是一个 Response 对象,可以使用以下任一方法获取响应内容:
| 方法 | 简要说明 |
|---|---|
| response.text() | 返回一个使用以文本为响应正文解析的 Promise |
| response.json() | 返回一个使用从 JSON 响应中解析的对象解析的 Promise |
| response.blob() | 返回一个使用以 Blob 对象为响应正文解析的 Promise |
| response.ArrayBuffer() | 返回一个使用以 ArrayBuffer 实例为响应正文解析的 Promise |
| response.formData() | 返回一个使用以 FormData 对象为响应正文解析的 Promise |
2.2 发起 POST 请求
POST 请求用于向服务器发送数据,如提交表单或调用 API 提交数据到服务器。通过 Fetch API 调用 POST 请求需要构造一个包含请求体的对象,并将这个对象作为第二个参数传递给 fetch 函数。
1const userData = { 2 username: 'example', 3 email: 'example@example.com' 4}; 5 6fetch('https://api.example.com/users', { 7 method: 'POST', 8 body: JSON.stringify(userData) 9}) 10.then(response => response.json()) 11.then(data => console.log('Success:', data)) 12.catch(error => console.error('出现错误:', error)); 13
使用 fetch() 发送 POST 请求的关键是指定要发送至服务器的数据,它可以采用多种格式,包括 JSON、FormData 和文本格式。在上述示例中,我们通过设置 method 为 POST 来发送 POST 请求,并在请求体 body 中发送 JSON 格式的数据。fetch 函数会将这些信息发送到服务器,并等待响应。
2.3 使用 async/await
Fetch API 支持 async/await 语法,可以更简洁地处理异步操作,如下所示。fetch 接收到的 response 是一个 Stream 对象,response.json() 是一个异步操作,取出所有内容,并将其转为 JSON 对象。
1const response = await fetch(url, options); 2const data = await response.json(); 3
三、Fetch API 的响应处理
3.1 处理 HTPP 响应
fetch 请求成功以后,得到的是一个 Response 对象,它对应服务器的 HTTP 响应。
1const res=await fetch(url) 2
Response 包含的数据通过 Stream 接口异步读取,但它还有一些同步属性,对应 HTTP 回应的标头信息(Headers),如下表所示。
| 标头属性 | 类型 | 简要说明 |
|---|---|---|
| Response.ok | boolean | 表示请求是否成功,true 对应的 HTTP 请求状态码200-299,false对应其他的状态码 |
| Response.status | number | 返回一个数字,表示HTTP响应的状态码 |
| Response.statusText | string | 表示HTTP响应的状态信息,例如请求成功以后,服务器返回 OK |
| Response.url | string | 返回请求的URL。如果URL存在跳转,该属性返回的是最终的URL |
| Response.type | string | 返回的是请求的类型。可能为以下值: basic:普通请求,即同源请求 cors:跨域请求 error:网络错误,主要用于Service Worker opaque:如果fetch请求的type属性为no-cors,就会返回这个值,表示发出的是简单的跨域请求 opaqueredirect:如果fetch请求的redirect属性设为manual,就会返回这个值 |
| Response.redirected | boolean | 表示请求是否有过重定向 |
3.2 处理请求响应状态
在处理请求响应时,我们首先检查响应状态是否成功(response.ok),如果不成功则抛出错误。fetch 发出请求后,只有网络错误或无法连接时才会报错,即使服务器返回的状态码 是4xx或5xx。只有通过 Response.status 属性得到 HTTP 响应的真实状态码时,才能判断请求是否成功。
1fetch('https://api.example.com/data') 2 .then(response => { 3 if (!response.ok) { 4 throw new Error('网络请求错误,' + response.statusText); 5 } 6 return response.json(); 7 }) 8 .then(data => console.log('Success:', data)) 9 .catch(error => console.error('出现错误:', error)); 10
3.3 处理不同的响应类型
当接收到服务器的响应后,通常需要解析响应体。Response 对象根据服务器返回的不同类型的数据,提供了不同的读取方法。这几个方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。
| 方法 | 简要说明 |
|---|---|
| response.text() | 获取文本字符串,主要用于获取文本数据,比如 HTML 文件 |
| response.json() | 获取 JSON 对象,主要用于获取服务器返回的 JSON 数据 |
| response.blob() | 获取二进制 Blob 对象 |
| response.formData() | 获取 FormData 表单对象,主要用于拦截用户提交的表单,修改某些数据后再提交给服务器 |
| response.arrayBuffer() | 得到二进制 ArrayBuffer 对象,主要用于获取流媒体文件 |
Fetch API 可以处理多种响应格式:
1// 处理JSON响应 2fetch('/api/data.json') 3 .then(response => response.json()) 4 .then(data => console.log(data)); 5 6// 处理文本响应 7fetch('/api/data.txt') 8 .then(response => response.text()) 9 .then(text => console.log(text)); 10 11// 处理Blob响应(如图片) 12fetch('/image.png') 13 .then(response => response.blob()) 14 .then(blob => { 15 const objectURL = URL.createObjectURL(blob); 16 document.getElementById('image').src = objectURL; 17 }); 18
注意,Response 是一个 Stream 对象,而 Stream 对象只能读取一次,读取完就没了。这意味着,上面的几个读取方法,只能使用一个,否则会报错。
四、高级 Fetch 用法
4.1 设置请求头
在使用Fetch API进行请求时,可以通过 Headers 对象来设置请求头。每个请求或响应都有一个与之关联的 Headers 对象,这个对象包含了请求头和响应头,例如 Content-Type、Authorization 等。
1fetch('https://example.com/api', { 2 method: 'POST', 3 headers: { 4 'Content-Type': 'application/json', 5 'Authorization': 'Bearer your-token' 6 }, 7 body: JSON.stringify({ name: 'John', age: 30 }) 8}) 9.then(response => response.json()) 10.then(data => console.log(data)) 11.catch(error => console.error('出现错误:', error)); 12
Response 对象还有一个 Response.headers 属性,指向一个Headers 对象,对应HTTP响应的所有标头。Headers 对象提供了以下方法来操作标头:
| 方法 | 简要说明 |
|---|---|
| Headers.get() | 根据指定的键名,返回键值 |
| Headers.has() | 返回一个布尔值,表示是否包含某个标头 |
| Headers.set() | 将指定的键名设置为新的键值,如果该键名不存在则会添加 |
| Headers.append() | 添加标头 |
| Headers.delete() | 删除标头 |
| Headers.keys() | 返回一个遍历器,可以依次遍历所有键名 |
| Headers.values() | 返回一个遍历器,可以依次遍历所有键值 |
| Headers.entries() | 返回一个遍历器,可以依次遍历所有键值对([key, value]) |
| Headers.forEach() | 依次遍历标头,每个标头都会执行一次参数函数 |
上面的有些方法可以修改标头,那是因为继承自 Headers 接口。有些标头不能通过headers属性设置,比如Content-Length、Cookie 、Host等等。它们是由浏览器自动生成,无法修改。这些方法中,最常用的是 response.headers.get(),用于读取某个标头的值。
1let response = await fetch(url); 2response.headers.get('Content-Type') 3
4.2 设置请求体参数
当需要发送POST请求时,经常需要向服务器发送一些数据。使用Fetch API可以很便捷地通过 body 属性发送请求体。
1// 发送JSON数据 2const data = JSON.stringify({ name: 'John', age: 30 }); 3 4const options = { 5 method: 'POST', 6 headers: { 7 'Content-Type': 'application/json', 8 'Accept': 'application/json' 9 }, 10 body: data 11}; 12 13fetch('https://example.com/api/users', options) 14.then(response => response.json()) 15.then(data => console.log(data)) 16.catch(error => console.error('Error:', error)); 17 18// 发送表单数据 19const formdata = new FormData(); 20formdata.append('username', 'john'); 21formdata.append('email', 'john@example.com'); 22 23fetch('https://example.com/api/register', { 24 method: 'POST', 25 body: formdata 26}) 27.then(response => response.json()) 28.then(data => console.log(data)) 29.catch(error => console.error('Error:', error)); 30
4.3 设置请求超时
Fetch API 本身不支持超时设置,但可以通过 AbortController 实现:
1const controller = new AbortController(); 2const signal = controller.signal; 3 4// 设置5秒超时 5const timeoutId = setTimeout(() => controller.abort(), 5000); 6 7fetch('https://api.example.com/data', { signal }) 8 .then(response => response.json()) 9 .then(data => { 10 clearTimeout(timeoutId); 11 console.log(data); 12 }) 13 .catch(error => { 14 if (error.name === 'AbortError') { 15 console.log('Request timed out'); 16 } else { 17 console.error('Other error:', error); 18 } 19 }); 20
4.4 跨域请求
如果需要进行跨域请求,可以在服务器端设置 CORS(Cross-Origin Resource Sharing)。在前端,也可以通过 credentials 选项来指定是否发送 cookies 等凭据。
1fetch('https://example.com/api', { 2 method: 'GET', 3 credentials: 'include' // 允许跨域请求时携带 cookie 4}) 5.then(response => response.json()) 6.then(data => console.log(data)) 7.catch(error => console.error('出现错误:', error)); 8
4.5 上传文件
如果表单里面有文件选择器,使用 Fetch 上传文件时,可以构造出一个表单,进行上传。
1const fileInput = document.querySelector('input[type="file"]'); 2 3const formData = new FormData(); 4formData.append('file', fileInput.files[0]); 5formData.append('username', 'exampleUser'); 6 7fetch('https://api.example.com/upload', { 8 method: 'POST', 9 body: formData 10 // 注意:不要手动设置Content-Type头,浏览器会自动设置正确的boundary 11}) 12.then(response => response.json()) 13.then(data => console.log('Upload success:', data)) 14.catch(error => console.error('Upload error:', error)); 15
4.6 请求取消
使用 AbortController 取消正在进行的请求:
1const controller = new AbortController(); 2 3// 开始请求 4fetch('https://api.example.com/data', { 5 signal: controller.signal 6}) 7.then(response => response.json()) 8.then(data => console.log(data)) 9.catch(error => { 10 if (error.name === 'AbortError') { 11 console.log('Request was aborted'); 12 } else { 13 console.error('Error:', error); 14 } 15}); 16 17// 在某个事件中取消请求 18document.getElementById('cancel-button').addEventListener('click', () => { 19 controller.abort(); 20}); 21
4.7 并发请求
使用 Promise.all 处理多个并发请求:
1async function fetchMultipleResources() { 2 try { 3 const [usersResponse, postsResponse] = await Promise.all([ 4 fetch('https://api.example.com/users'), 5 fetch('https://api.example.com/posts') 6 ]); 7 8 if (!usersResponse.ok || !postsResponse.ok) { 9 throw new Error('One or more requests failed'); 10 } 11 12 const users = await usersResponse.json(); 13 const posts = await postsResponse.json(); 14 15 console.log('Users:', users); 16 console.log('Posts:', posts); 17 18 // 合并数据并更新UI 19 displayCombinedData(users, posts); 20 } catch (error) { 21 console.error('Error fetching data:', error); 22 } 23} 24 25function displayCombinedData(users, posts) { 26 // 实现数据合并和显示逻辑 27} 28
五、总结
Fetch API 是一个强大而简洁的网络请求 API,它基于 Promise 实现,提供了更好的可读性和可维护性。通过学习和掌握 Fetch API 的基本用法和请求方法,我们可以更轻松地发起网络请求并处理响应结果。同时,我们也需要注意 Fetch API 的一些限制和常见问题,并采取相应的措施来解决它们。
《使用Fetch API 探索前后端数据交互》 是转载文章,点击查看原文。