当前位置: 首页 > 工具软件 > nutch-ajax > 使用案例 >

【Ajax】Fetch请求

商松
2023-12-01


参考自:https://zh.javascript.info/network

ajax请求可以做到在没有重新加载页面下,进行数据的更新例如做到:

  • 提交订单,
  • 加载用户信息,
  • 从服务器接收最新的更新,

也是前端用于发起网络请求的比较好的主流方式,ajax请求主要包括两种
XML和fetch,XML可能接触的比较多,下面主要学习fetch请求。

fetch() 方法是一种现代通用的方法,那么我们就从它开始吧。旧版本的浏览器不支持它(可以 polyfill),但是它在现代浏览器中的支持情况很好。

基本语法

let promise = fetch(url,[options])

url:要访问的url
options:可选参数:method、header等

  • 没有 options,这就是一个简单的 GET 请求,下载 url 的内容。
  • 浏览器立即启动请求,并返回一个该调用代码应该用来获取结果的 promise。

获取响应有两个阶段:

第一阶段, 当服务器发送了响应头(response header),fetch 返回的 promise 就使用内建的 Response class 对象来对响应头进行解析。

检查响应头,检查http状态确定请求是否成功,当前还没有响应体

如果 fetch 无法建立一个 HTTP 请求,例如网络问题,亦或是请求的网址不存在,那么 promise 就会 reject。异常的 HTTP 状态,例如 404 或 500,不会导致出现 error。

我们可以在 response 的属性中看到 HTTP 状态:
status —— HTTP 状态码,例如 200。
ok —— 布尔值,如果 HTTP 状态码为 200-299,则为 true。

let response = await fetch(url);

if(response.ok){
let json = await response.json();
}else{
	alert("HTTP-ERROR" + response.status)
}

第二阶段, 为了获取 response body,我们需要使用一个其他的方法调用。

Response 提供了多种基于 promise 的方法,来以不同的格式访问 body:

response.text() —— 读取 response,并以文本形式返回 response,
response.json() —— 将 response 解析为 JSON 格式,
response.formData() —— 以 FormData 对象(在 下一章 有解释)的形式返回 response,
response.blob() —— 以 Blob(具有类型的二进制数据)形式返回 response,
response.arrayBuffer() —— 以 ArrayBuffer(低级别的二进制数据)形式返回 response,
另外,response.body 是 ReadableStream 对象,它允许你逐块读取 body,我们稍后会用一个例子解释它。

如果要创建一个post请求,

let user = {
  name: 'John',
  surname: 'Smith'
};

let response = await fetch('/article/fetch/post/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json;charset=utf-8'
  },
  body: JSON.stringify(user)
});

let result = await response.json();
alert(result.message);

典型的fetch请求由两个await调用执行

let response = await fetch(url,options);
let result = await response.json();

//或者以promise
fetch(url,options)
	.then(response => response.json())
	.then(result => )

FormData

FormData对象是表示HTML表单数据的对象,

FormData 的特殊之处在于网络方法(network methods),例如 fetch 可以接受一个 FormData 对象作为 body。它会被编码并发送出去,带有 Content-Type: multipart/form-data。

从服务器角度来看,它就像是一个普通的表单提交。

FormData对象用于捕获HTML表单,并使用fetch或其他网络方法提交

Fetch:下载进度

fetch 方法允许去跟踪 下载 进度。

请注意:到目前为止,fetch 方法无法跟踪 上传 进度。对于这个目的,请使用 XMLHttpRequest,我们在后面章节会讲到。

与 response.text(),response.json() 和其他方法同,response.body 给予了对进度读取的完全控制,我们可以随时计算下载了多少。

Fetch:中止

正如我们所知道的,fetch 返回一个 promise。JavaScript 通常并没有“中止” promise 的概念。那么我们怎样才能取消一个正在执行的 fetch 呢?例如,如果用户在我们网站上的操作表明不再需要某个执行中的 fetch。

为此有一个特殊的内建对象:AbortController。它不仅可以中止 fetch,还可以中止其他异步任务

let controller = new AbortController();

但是有价值的是,fetch 知道如何与 AbortController 对象一起工作。它们是集成在一起的。

为了能够取消 fetch,请将 AbortController 的 signal 属性作为 fetch 的一个可选参数(option)进行传递:

let controller = new AbortController();
fetch(url,{
	signal:controller.signal
});

controller.abort();

fetch 方法知道如何与 AbortController 一起工作。它会监听 signal 上的 abort 事件。

现在,想要中止 fetch,调用 controller.abort() 即可。

当一个 fetch 被中止,它的 promise 就会以一个 error AbortError reject,因此我们应该对其进行处理,例如在 try…catch 中。

et controller = new AbortController();

setTimeout(()=> controller.abort(),1000);

try {
    let response = await fetch('/article/fetch-abort/demo/hang',{
        signal:controller.signal
    });
} catch(err) {
    if(err.name == 'AbortError'){
        alert("Aborted");
    }else{
        throw err;
    }
}

AbortController 是可伸缩的。它允许一次取消多个 fetch。

let urls = [];

let controller = new AbortController();

let fetchJobs = urls.map(url => fetch(url,{
    signal:controller.signal
}));

let results = await Promise.all(fetchJobs);
  • AbortController 是一个简单的对象,当 abort() 方法被调用时,会在自身的 signal 属性上生成 abort 事件(并将 signal.aborted 设置为 true)。
  • fetch 与之集成:我们将 signal 属性作为可选参数(option)进行传递,之后 fetch 会监听它,因此它能够中止 fetch。
  • 我们可以在我们的代码中使用 AbortController。“调用 abort()” → “监听 abort 事件”交互简单且通用。即使没有 fetch,我们也可以使用它。

Fetch:跨源请求

如果我们向另一个网站发送 fetch 请求,则该请求可能会失败.

同源:协议、端口、域名一致

跨源请求,就是三者不是完全一致的,发送到其他域的请求,就需要来自远程端的特殊leader

CORS:跨资源共享(Cross-Origin Resource Shering)

有两种类型的跨源请求:
1、安全类型
2、所有其他请求

安全的方法:GET,POST 或 HEAD

安全的 header —— 仅允许自定义下列 header:
Accept,
Accept-Language,
Content-Language,
Content-Type 的值为 application/x-www-form-urlencoded,multipart/form-data 或 text/plain。

本质区别在于,可以使用 form或 script进行安全请求,而无需任何其他特殊方法。

当我们尝试发送一个非安全请求时,浏览器会发送一个特殊的“预检(preflight)”请求到服务器 —— 询问服务器,你接受此类跨源请求吗?

并且,除非服务器明确通过 header 进行确认,否则非安全请求不会被发送。

用于安全请求的 CORS

如果一个请求是跨源的,浏览器始终会向其添加 Origin header。

例如,如果我们从 https://javascript.info/page 请求 https://anywhere.com/request,请求的 header 将如下所示:

GET /request
Host: anywhere.com
Origin: https://javascript.info
...

正如你所看到的,Origin 包含了确切的源(domain/protocol/port),没有路径(path)。
服务器可以检查 Origin,如果同意接受这样的请求,就会在响应中添加一个特殊的 header Access-Control-Allow-Origin

浏览器在这里扮演受被信任的中间人的角色:

它确保发送的跨源请求带有正确的 Origin。
它检查响应中的许可 Access-Control-Allow-Origin,如果存在,则允许 JavaScript 访问响应,否则将失败并报错。

200 OK
Content-Type:text/html; charset=UTF-8
Access-Control-Allow-Origin: https://javascript.info

对于跨源请求,默认情况下,JavaScript 只能访问“安全的” response header:

  • cache-control
  • content-language
  • content-type
  • expires
  • last-modified
  • pragma

“非安全”请求
我们可以使用任何 HTTP 方法:不仅仅是 GET/POST,也可以是 PATCH,DELETE 及其他。

预检请求使用 OPTIONS 方法,它没有 body,但是有三个 header:

  • origin
  • Access-Control-Request-Method
  • Access-Control-Request-Headers

服务器返回:

  • Access-Control-Allow-Origin
  • Access-Control-Allow-Method
  • Access-Control-Allow-Headers
  • Access-Control-Max-Age

当发起一个非安全请求

let response = await fetch('https://site.com/service.json', {
  method: 'PATCH',
  headers: {
    'Content-Type': 'application/json',
    'API-Key': 'secret'
  }
});

Step 1 预检请求(preflight request)
在发送我们的请求前,浏览器会自己发送如下所示的预检请求:

OPTIONS /service.json
Host: site.com
Origin: https://javascript.info
Access-Control-Request-Method: PATCH
Access-Control-Request-Headers: Content-Type,API-Key

Step 2 预检响应(preflight response)
服务应响应状态 200 和 header:

Access-Control-Allow-Origin: https://javascript.info
Access-Control-Allow-Methods: PATCH
Access-Control-Allow-Headers: Content-Type,API-Key。

Step 3 实际请求(actual request)
预检成功后,浏览器现在发出主请求。这里的过程与安全请求的过程相同。

PATCH /service.json
Host: site.com
Content-Type: application/json
API-Key: secret
Origin: https://javascript.info

主请求具有 Origin header(因为它是跨源的):

Step 4 实际响应(actual response)
服务器不应该忘记在主响应中添加 Access-Control-Allow-Origin。成功的预检并不能免除此要求:

Access-Control-Allow-Origin: https://javascript.info

Fetch API

 类似资料: