使用Fetch API 探索前后端数据交互

0 阅读11分钟

image

前言

  在当今的 Web 开发中,前端与后端的数据交互是构建动态应用的核心。API 是连接不同软件应用的重要桥梁,允许开发者通过 HTTP 请求与服务器交互,高效调用API数据对于构建现代 Web 应用至关重要。传统的页面刷新方式已经无法满足用户对流畅体验的需求,而 Fetch API 的出现为 JavaScript 带来了全新的生命力。

一、Fetch API 概述

1.1 Fetch API 是什么❓

  Fetch API 是现代浏览器提供的一个用于发起网络请求的接口,用于发起 HTTP 请求。它提供简洁的异步API,使开发者能够以更现代的方式与服务器交互。它是传统的 XMLHttpRequest 的替代品,提供了更简洁、更强大的功能。基于 Promise 实现,使异步操作更加直观。

image

  相比于传统的 XMLHttpRequest 更加强大、灵活且易于使用。Fetch 基于 Promise 设计,使得异步请求的处理更加优雅。

特性Fetch APIXMLHttpRequest
语法基于 Promise,更简洁回调函数,较复杂
请求/响应对象标准化非标准化
默认携带 Cookie不携带携带
超时控制需要额外实现原生支持
取消请求使用 AbortController原生支持
进度事件有限支持完整支持

1.2 Fetch 的基本语法

  Fetch API 的基本用法是通过调用 fetch() 函数并传入一个 URL 作为参数来发起网络请求。该函数返回一个Promise对象,可以在其then()方法中处理请求成功的情况,在catch()方法中处理请求失败的情况。Fetch API 最基本的形式如下所示。

fetch(url, options)
	.then(response => response.json()) // 解析 JSON 数据
  .then(data => console.log(data))   // 处理数据
	.catch(error => console.error('出现错误:', error)); // 错误处理

  上述代码示例展示了使用 Fetch API 发起一个请求,返回的 Promise 解析为响应对象,进而能访问响应体数据。处理响应体通常包含 JSON 数据,通过 .json() 方法解析。如果请求失败,fetch 返回的 promise 会拒绝,并将错误信息传给 catch 方法。

1.3 fetch 配置选项

  fetch 接受第二个可选参数,一个可以控制不同配置的对象,常见属性如下表所示。

配置项简要描述常用值
method请求的 HTTP 方法,默认方法为GETGET、POST、PUT、PATCH、DELETE
headers请求中 HTTP 标头
body请求体。
请注意,使用 GET 和 HEAD 方法的请求不能有正文
mode指定请求的模式。cors:默认值,允许跨域请求
same-origin:只允许同源请求。
no-cors:不能添加跨域的复杂标头,相当于提交表单所能发出的请求
credentials指定是否发送 Cookiesame-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送
include:不管同源请求,还是跨域请求,一律发送 Cookie
omit:一律不发送
cache指定如何处理缓存default:默认值,先在缓存里面寻找匹配的请求
no-store:直接请求远程服务器,并且不更新缓存
reload:直接请求远程服务器,并且更新缓存
no-cache:
force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器
only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误
redirect如何处理 HTTP 重定向响应,默认设置为followfollow、error、manual
referrer包含请求的反向链接的字符串,默认为空字符串
referrerPolicy指定用于请求的反向链接政策
signalAbortSignal 对象实例,支持接口中止请求
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)部分传递给服务器的。

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('出现错误:', error));

  在上述例子中,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 函数。

const userData = {
  username: 'example',
  email: 'example@example.com'
};

fetch('https://api.example.com/users', {
  method: 'POST',
  body: JSON.stringify(userData)
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('出现错误:', error));

  使用 fetch() 发送 POST 请求的关键是指定要发送至服务器的数据,它可以采用多种格式,包括 JSON、FormData 和文本格式。在上述示例中,我们通过设置 method 为 POST 来发送 POST 请求,并在请求体 body 中发送 JSON 格式的数据。fetch 函数会将这些信息发送到服务器,并等待响应。

2.3 使用 async/await

  Fetch API 支持 async/await 语法,可以更简洁地处理异步操作,如下所示。fetch 接收到的 response 是一个 Stream 对象,response.json() 是一个异步操作,取出所有内容,并将其转为 JSON 对象。

const response = await fetch(url, options);
const data = await response.json();

三、Fetch API 的响应处理

3.1 处理 HTPP 响应

  fetch 请求成功以后,得到的是一个 Response 对象,它对应服务器的 HTTP 响应。

const res=await fetch(url)

  Response 包含的数据通过 Stream 接口异步读取,但它还有一些同步属性,对应 HTTP 回应的标头信息(Headers),如下表所示。

标头属性类型简要说明
Response.okboolean表示请求是否成功,true 对应的 HTTP 请求状态码200-299,false对应其他的状态码
Response.statusnumber返回一个数字,表示HTTP响应的状态码
Response.statusTextstring表示HTTP响应的状态信息,例如请求成功以后,服务器返回 OK
Response.urlstring返回请求的URL。如果URL存在跳转,该属性返回的是最终的URL
Response.typestring返回的是请求的类型。可能为以下值:
 basic:普通请求,即同源请求
 cors:跨域请求
 error:网络错误,主要用于Service Worker
 opaque:如果fetch请求的type属性为no-cors,就会返回这个值,表示发出的是简单的跨域请求
 opaqueredirect:如果fetch请求的redirect属性设为manual,就会返回这个值
Response.redirectedboolean表示请求是否有过重定向

image

3.2 处理请求响应状态

  在处理请求响应时,我们首先检查响应状态是否成功(response.ok),如果不成功则抛出错误。fetch 发出请求后,只有网络错误或无法连接时才会报错,即使服务器返回的状态码 是4xx或5xx。只有通过 Response.status 属性得到 HTTP 响应的真实状态码时,才能判断请求是否成功。

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('网络请求错误,' + response.statusText);
    }
    return response.json();
  })
  .then(data => console.log('Success:', data))
  .catch(error => console.error('出现错误:', error));

3.3 处理不同的响应类型

  当接收到服务器的响应后,通常需要解析响应体。Response 对象根据服务器返回的不同类型的数据,提供了不同的读取方法。这几个方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。

方法简要说明
response.text()获取文本字符串,主要用于获取文本数据,比如 HTML 文件
response.json()获取 JSON 对象,主要用于获取服务器返回的 JSON 数据
response.blob()获取二进制 Blob 对象
response.formData()获取 FormData 表单对象,主要用于拦截用户提交的表单,修改某些数据后再提交给服务器
response.arrayBuffer()得到二进制 ArrayBuffer 对象,主要用于获取流媒体文件

  Fetch API 可以处理多种响应格式:

// 处理JSON响应
fetch('/api/data.json')
  .then(response => response.json())
  .then(data => console.log(data));

// 处理文本响应
fetch('/api/data.txt')
  .then(response => response.text())
  .then(text => console.log(text));

// 处理Blob响应(如图片)
fetch('/image.png')
  .then(response => response.blob())
  .then(blob => {
    const objectURL = URL.createObjectURL(blob);
    document.getElementById('image').src = objectURL;
  });

注意,Response 是一个 Stream 对象,而 Stream 对象只能读取一次,读取完就没了。这意味着,上面的几个读取方法,只能使用一个,否则会报错。

四、高级 Fetch 用法

4.1 设置请求头

  在使用Fetch API进行请求时,可以通过 Headers 对象来设置请求头。每个请求或响应都有一个与之关联的 Headers 对象,这个对象包含了请求头和响应头,例如 Content-Type、Authorization 等。

fetch('https://example.com/api', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer your-token'
    },
    body: JSON.stringify({ name: 'John', age: 30 })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('出现错误:', error));

  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(),用于读取某个标头的值。

let response =  await  fetch(url);  
response.headers.get('Content-Type')

4.2 设置请求体参数

  当需要发送POST请求时,经常需要向服务器发送一些数据。使用Fetch API可以很便捷地通过 body 属性发送请求体。

// 发送JSON数据
const data = JSON.stringify({ name: 'John', age: 30 });
 
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  body: data
};
 
fetch('https://example.com/api/users', options)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
 
// 发送表单数据
const formdata = new FormData();
formdata.append('username', 'john');
formdata.append('email', 'john@example.com');
 
fetch('https://example.com/api/register', {
  method: 'POST',
  body: formdata
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

4.3 设置请求超时

  Fetch API 本身不支持超时设置,但可以通过 AbortController 实现:

const controller = new AbortController();
const signal = controller.signal;

// 设置5秒超时
const timeoutId = setTimeout(() => controller.abort(), 5000);

fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => {
    clearTimeout(timeoutId);
    console.log(data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Request timed out');
    } else {
      console.error('Other error:', error);
    }
  });

4.4 跨域请求

  如果需要进行跨域请求,可以在服务器端设置 CORS(Cross-Origin Resource Sharing)。在前端,也可以通过 credentials 选项来指定是否发送 cookies 等凭据。

fetch('https://example.com/api', {
    method: 'GET',
    credentials: 'include' // 允许跨域请求时携带 cookie
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('出现错误:', error));

4.5 上传文件

  如果表单里面有文件选择器,使用 Fetch 上传文件时,可以构造出一个表单,进行上传。

const fileInput = document.querySelector('input[type="file"]');

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('username', 'exampleUser');

fetch('https://api.example.com/upload', {
  method: 'POST',
  body: formData
  // 注意:不要手动设置Content-Type头,浏览器会自动设置正确的boundary
})
.then(response => response.json())
.then(data => console.log('Upload success:', data))
.catch(error => console.error('Upload error:', error));

4.6 请求取消

  使用 AbortController 取消正在进行的请求:

const controller = new AbortController();

// 开始请求
fetch('https://api.example.com/data', {
  signal: controller.signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
  if (error.name === 'AbortError') {
    console.log('Request was aborted');
  } else {
    console.error('Error:', error);
  }
});

// 在某个事件中取消请求
document.getElementById('cancel-button').addEventListener('click', () => {
  controller.abort();
});

4.7 并发请求

  使用 Promise.all 处理多个并发请求:

async function fetchMultipleResources() {
  try {
    const [usersResponse, postsResponse] = await Promise.all([
      fetch('https://api.example.com/users'),
      fetch('https://api.example.com/posts')
    ]);

    if (!usersResponse.ok || !postsResponse.ok) {
      throw new Error('One or more requests failed');
    }

    const users = await usersResponse.json();
    const posts = await postsResponse.json();

    console.log('Users:', users);
    console.log('Posts:', posts);

    // 合并数据并更新UI
    displayCombinedData(users, posts);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

function displayCombinedData(users, posts) {
  // 实现数据合并和显示逻辑
}

五、总结

  Fetch API 是一个强大而简洁的网络请求 API,它基于 Promise 实现,提供了更好的可读性和可维护性。通过学习和掌握 Fetch API 的基本用法和请求方法,我们可以更轻松地发起网络请求并处理响应结果。同时,我们也需要注意 Fetch API 的一些限制和常见问题,并采取相应的措施来解决它们。

image