前言

XMLHttpRequest 是 JavaScript built-in 的一个 class,用于发送 HTTP 请求,俗称 AJAX。

这几年 XMLHttpRequest 已经逐渐被 Fetch 取代了,只剩下一个功能目前 Fetch 还做不到 -- 获取上传进度,因此 XMLHttpRequest 还是得学起来。

Simple Get Request & Response

首先实例化 XMLHttpRequest 对象

const request = new XMLHttpRequest();

接着设置 request method 和 URL

request.open('GET', 'https://192.168.1.152:44300/products');

接着监听 load 事件

request.addEventListener('load', () => {
console.log('status', request.status);
console.log('response', request.response);
});

load 事件会在 response 下载完毕后发布。

接着发送 request

request.send();

效果

Request with Query Parameters

XMLHttpRequest 没有 built-in 对 Query Parameters 的处理。

我们需要借助 URLSearchParams。

const request = new XMLHttpRequest();
const searchParams = new URLSearchParams({
key1: 'value1',
});
const queryString = '?' + searchParams.toString();
request.open('GET', 'https://192.168.1.152:44300/products' + queryString);

然后把 Query String 拼接到 request URL 就可以了。

Request with Header

通过 request.setRequestHeader 方法就可以添加 header 了。

const request = new XMLHttpRequest();
request.open('GET', 'https://192.168.1.152:44300/products');
request.setRequestHeader('Accept', 'application/json');

Auto Parse JSON Response

上面的例子,request.response 是一个 string 类型,我们需要手动 parse JSON。

request.addEventListener('load', () => {
// status 是 number 类型
console.log('status', request.status); // 200 // response 是 string 类型
console.log('response', request.response); // '[{"name":"iPhone14"},{"name":"iPhone15"}]' const products = JSON.parse(request.response) as { name: string }[]; console.log(products[0].name); // 'iPhone14'
});

我们可以通过设置 request.responseType 让 XMLHttpRequest 自动替我们 parse JSON。

request.responseType = 'json';
request.addEventListener('load', () => {
// response 变成了 Array 类型
const products: { name: string }[] = request.response;
console.log(products[0].name); // 'iPhone14'
});

Response Header

request.getResponseHeader 方法和 request.getAllResponseHeaders 方法

request.addEventListener('load', () => {
console.log(request.getResponseHeader('Content-Type')); // 'application/json; charset=utf-8'
console.log('response content type', request.getAllResponseHeaders()); // 'content-type: application/json; charset=utf-8'
});

如果遇到重复的 header 那么它会合并起来

key 是 'cutom',value 是 'a, b'

Cross-Origin 跨域请求携带 Cookie

跨域主要是服务端的职责,不熟悉的可以看这篇 ASP.NET Core – CORS (Cross-Origin Resource Sharing)

客户端和跨域有关的是 Cookie。

同跨 (same-origin) 请求,XMLHttpRequest 会自动携带 Cookie。

跨域 (cross-origin) 请求,默认不携带 Cookie,如果我们希望它携带 Cookie 可以设置 withCredentials。

request.withCredentials = true;

注:withCredentials 只对 cross-origin 有效,same-origin 请求即便 withCredentials = false 依然会携带 Cookie。

Request Error

Request Error 指的是请求失败,通常是 offline 造成的。

status 400-5xx 这些可不算 Request Error 哦,因为这些已经是有 response 成功了,只是在 status 被区分是有问题的。

要处理 Request Error,监听 error 事件就可以了。

request.addEventListener('error', () => {
console.log('Network error, possibly offline!');
});

Abort Request

我们可以在任意时间终止一个 request。

request.addEventListener('abort', () => {
console.log('The request has been aborted.');
}); setTimeout(() => {
request.abort();
}, 2000);

两秒钟后执行 request.abort 方法,终止请求,同时发布 abort 事件。

另外,客户端也会通知服务端,请求被终止了。

ASP.NET Core 有一个 cancellationToken,可以随时查看是否客户端已经终止请求, 如果已经终止,那就不需要继续执行了。

Request Timeout

我们可以设置请求的最长时间,如果在时间内没有收到 response 或者 response body 来不及被下载完,这都算请求超时。

request.timeout = 5000; // unit 是 milliseconds
request.addEventListener('timeout', () => {
console.log('Timeout, the request is taking more than five seconds.');
});

监听 timeout 事件可以处理超时请求。

load 和 loaded 事件

load 事件在请求失败 (error, abort, timeout) 时是不会发布的。

loaded 事件则无论请求成功还是失败都会发布。

注:response status 400-599 依然算是请求成功哦。

Download File

除了请求 JSON 数据,偶尔我们也会需要下载文件。

download txt file

const request = new XMLHttpRequest();
request.open('GET', 'https://192.168.1.152:44300/data.txt');
request.responseType = 'arraybuffer';
request.addEventListener('load', () => {
const memoryStream: ArrayBuffer = request.response;
const bytes = new Uint8Array(memoryStream);
const textDecoder = new TextDecoder();
const text = textDecoder.decode(bytes);
console.log(text); // 'Hello World'
});
request.send();

关键是 responseType 设置成了 'arraybuffer'。

request.response 的类型变成了 ArrayBuffer,通过 Uint8Array 和 TextDecoder 从 ArrayBuffer 读取 data.txt 的内容。

Download Video

Video 通常 size 比较大,用 ArrayBuffer 怕内存会不够,所以用 Blob 会比较合适。

const request = new XMLHttpRequest();
request.open('GET', 'https://192.168.1.152:44300/video.mp4');
request.responseType = 'blob';
request.addEventListener('load', () => {
const blob: Blob = request.response;
console.log(blob.size / 1024); // 124,645 kb
console.log(blob.type); // video/mp4
});
request.send();

responseType = 'blob'

download progress

文件大下载慢,最好可以显示进度条

request.addEventListener('progress', e => {
const percentage = ((e.loaded / e.total) * 100).toFixed() + '%';
console.log(percentage);
});

通过监听 progress 事件,可以获取到 total size 和已下载的 size,progress 事件会触发多次。

partial data on downloading

如果把 responseType 设置成 'text',request.response 会在 progress 的过程中逐渐被添加。

request.responseType = 'text';
request.addEventListener('progress', () => {
console.log(request.response);
});

只有 'text' 才会这样,'blob', 'arraybuffer' 在 progress 阶段 request.response 始终是 null。

所以,这个功能其实没有什么鸟用,因为如果我们请求的是一个 video.mp4,使用 ‘text’ 的话,video 原本的 binary 会被强制转换成 string,而这个过程很可能会破坏掉原本的 binary,导致 binary 无可还原,video 就毁了。

POST Request

POST 和 GET 大同小异

POST JSON Data

const request = new XMLHttpRequest();
request.open('POST', 'https://192.168.1.152:44300/products');
request.setRequestHeader('Accept', 'application/json');
request.setRequestHeader('Content-Type', 'application/json');
request.responseType = 'json';
request.addEventListener('load', () => {
console.log(request.status); // 201
console.log(request.response); // { id: 1, name: 'iPhone12' }
});
const product = { name: 'iPhone12' };
const json = JSON.stringify(product);
request.send(json);

JSON 格式需要添加 request header 'Content-Type',然后是 request.send 方法传入要发送的 JSON 数据就可以了。

POST FormData or FormUrlEncoded (multipart/form-data or application/x-www-form-urlencoded)

POST FormData or FormUrlEncoded 和 POST JSON data 大同小异

// POST multipart/form-data
const productFormData = new FormData();
productFormData.set('name', 'iPhone12');
request.send(productFormData); // POST application/x-www-form-urlencoded
const productFormUrlEncoded = new URLSearchParams({
name: 'iPhone12',
});
request.send(productFormUrlEncoded);

只是把 send 的数据从 JSON 换成 FormData or URLSearchParams 就可以了。

另外,FormData or FormUrlEncoded 不需要设置 request header 'Content-Type',游览器会依据发送的数据类型自动添加,JSON 之所以需要是因为游览器把 JSON 视为 text/plain。

POST Binary (Blob)

FormData 支持 Blob 类型的 value,所以我们可以使用 FormData 上传二进制文件。

const productFormData = new FormData();
productFormData.set('name', 'iPhone12'); const productDocument = 'Product Detail';
const textEncoder = new TextEncoder();
const bytes = textEncoder.encode(productDocument);
const blob = new Blob([bytes], {
type: 'text/plain',
});
productFormData.set('document', blob);
request.send(productFormData);

或者直接发送 Blob 也是可以的。

const productDocument = 'Product Detail';
const textEncoder = new TextEncoder();
const bytes = textEncoder.encode(productDocument);
const blob = new Blob([bytes], {
type: 'text/plain', // 如果二进制没有明确类型,type 就放 application/octet-stream
});
request.send(blob);

upload progress

和 download 是同一个原理

request.upload.addEventListener('progress', e => {
const percentage = ((e.loaded / e.total) * 100).toFixed() + '%';
console.log(percentage);
});

和 download 不同的是,它监听的对象是 request.upload 而不是 request。

Request ReadyState

request.readyState 反应了当前 Reqest 的阶段。readystatechange 事件会在 Request 有动静的时候发布,比如 state change 的时候或者 state Loading 下载到新数据的时候。

0 是初始阶段。

1 是设置了 request method 和 URL 之后

2.是 response header 下载完毕

3 是 downloading response body

4 是 response body 下载完毕

request.addEventListener('readystatechange', () => {
console.log(request.readyState);
});

有 3 个知识点:

  1. request 发出后,无论成功还是失败 (error, abort, time),readyState 最终都会变成 DONE。
  2. readystatechange 会在 LOADING 阶段 (也就下载文件的时候) 发布多次。
  3. response header 会先被下载,然后发布 readystatechange HEADERS_RECEIVED。
    如果我们想最快的获取 response header 资料,可以把监听从 load 事件换到 readystatechange 事件。
    request.addEventListener('readystatechange', () => {
    if (request.readyState === 2) {
    console.log(request.getResponseHeader('Content-Length'));
    }
    else if (request.readyState === 4 && request.status !== 0) {
    console.log(request.status);
    console.log(request.response);
    }
    });

    readState === 4 是一定会发布的,所以如果我们只想要监听成功的请求,这里需要过滤 status !== 0。

JavaScript – XMLHttpRequest的更多相关文章

  1. How to get blob data using javascript XmlHttpRequest by sync

    Tested: Firefox 33+ OK Chrome 38+ OK IE 6 -- IE 10 Failed Thanks to 阮一峰's blog: http://www.ruanyifen ...

  2. javascript XMLHttpRequest对象全面剖析

    转载:http://www.jb51.net/article/23175.htm 一. 引言 异步JavaScript与XML(AJAX)是一个专用术语,用于实现在客户端脚本与服务器之间的数据交互过程 ...

  3. [javascript]XMLHttpRequest GET/SET HTTP头与改变HTTP METHOD示例代码

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  4. JavaScript——XMLHttpRequest 家族

    https://www.zhangxinxu.com/wordpress/2013/10/understand-domstring-document-formdata-blob-file-arrayb ...

  5. [JavaScript] XMLHttpRequest记录

    XMLHttpRequest 使用 XMLHttpRequest 对象可以和服务器进行交互,可以获取到数据,而无需让整个页面进行刷新.这样 web 页面可以做到只更新局部页面,降低了对用户操作的影响. ...

  6. [Javascript]XMLHttpRequest对象实现下载进度条

    摘要 可以通过设置一个XMLHttpRequest对象的responseType属性来改变一个从服务器上返回的响应的数据类型.可用的属性值为空字符串 (默认), "arraybuffer&q ...

  7. javascript XMLHttpRequest 对象的open() 方法参数说明

    下文是从w3c上摘录下来的,其中参数 method 说明的很简短,不是很理解,所以又找了些资料作为补充.文中带括号部分. XMLHttpRequest.open() 初始化 HTTP 请求参数 语法o ...

  8. javascript AJAX简单原理及什么是ajax

    AJAX简单原理供初学者理解 AJAX的原理: Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面.这其 ...

  9. 06XML JavaScript

    1. XML JavaScript XMLHttpRequest 对象 XML DOM (XML Document Object Model) 定义了访问和操作 XML 文档的标准方法.

  10. comp.lang.javascript FAQ [zz]

    comp.lang.javascript FAQ Version 32.2, Updated 2010-10-08, by Garrett Smith FAQ Notes 1 Meta-FAQ met ...

随机推荐

  1. CF916C 题解

    CF916C 题解 思路 思考发现,如果我们让很多边的边权变得非常大,而故意留下 \(1\) 到 \(n\) 的某一条路径,使整条路径之和甚至还没有剩下一条边的权值大,这条路径显然就是最短路了. 更重 ...

  2. 2023/4/21 SCRUM个人博客

    1.我昨天的任务 学习信号和槽的定义并完善UI界面的基础知识 2.遇到了什么困难 难以理解design的设计理念 3.我今天的任务 学习如何使用QTdesign,并完善UI

  3. Jmeter函数助手5-RandomFromMultipleVars

    RandomFromMultipleVars函数用于获取指定变量的随机变量值. Source Variable(s) (use | as separator):传入指定的变量名称,这里的变量可以是单值 ...

  4. 【Mybatis-Plus】使用QueryWrapper作为自定义SQL的条件参数

    发现同事的自定义SQL写法是这样的 连表之后使用的条件是 ${ew.customSqlSegment} @Param声明的常量: /** * wrapper 类 */ String WRAPPER = ...

  5. AI辅助教学,甚至AI教学,逐渐成为可能

    看新闻,有感: 太火爆!一门课,两个月吸粉十五万人!企业纷纷布局!千亿级大市场,来了?

  6. 并行化强化学习 —— 初探 —— 并行reinforce算法的尝试 (上篇:强化学习在多仿真环境下单步交互并行化设计的可行性)

    强化学习由于难收敛所以训练周期较长,同时由于强化学习在训练过程中起训练数据一般都为实时生成的,因此在训练的同时算法还需要生成待训练的数据,强化学习算法的基本架构可以视作下图:(取自:深度学习中使用Te ...

  7. 【Playwright+Python】系列教程(七)使用Playwright进行API接口测试

    playwright也是可以做接口测试的,但个人觉得还是没有requests库强大,但和selenium相比的话,略胜一筹,毕竟支持API登录,也就是说可以不用交互直接调用接口操作了. 怎么用 既然是 ...

  8. Spring Boot 中使用 JSON Schema 来校验复杂JSON数据

    JSON是我们编写API时候用于数据传递的常用格式,那么你是否知道JSON Schema呢? 在数据交换领域,JSON Schema 以其强大的标准化能力,为定义和规范 JSON 数据的结构与规则提供 ...

  9. cdq分治 基础篇

    简介 前置芝士:归并排序. \(cdq\) 分治是个离线算法,可以解决三维偏序或者优化 \(dp\). 陌上花开 维护三维偏序有个口诀:一维排序,二维归并,三维数据结构. 考虑第一维直接排序解决掉,然 ...

  10. Camera | 10.linux驱动 led架构-基于rk3568

    前面文章我们简单给大家介绍了如何移植闪光灯芯片sgm3141,该驱动依赖了led子系统和v4l2子系统. V4L2可以参考前面camera系列文章,本文主要讲述led子系统. 一.LED子系统框架 L ...