JavaScript高级程序设计-网络请求与远程资源

网络请求与远程资源

XMLHttpRequest 对象

概述

const xhr = new XMLHttpRequest()

使用步骤:

    1. xhr.open(method,url,true)
    1. xhr.send(data)
    1. xhr.onreadystatechange

常用的 xhr 属性:

  • xhr.status: 状态码,成功 200
  • xhr.statusText: 状态描述,“OK”
  • xhr.responseText: 响应体文本
  • xhr.readyState: 值为 4 表示已经获取到响应
let xhr = new XMLHttpRequest();

const method = "get";
const url = "http://localhost:3000/";

// 1. 定义请求方法,请求url,是否是异步
xhr.open(method, url, true);
// 2. 真正发送请求,send接收请求数据
xhr.send(null);

// xhr的readystatechange事件
// xhr.readyState:4表明已获取到响应
// xhr.status 表示响应的状态码,200表示成功
// xhr.statusText 表示响应状态的描述,如果是200,则对应是"OK"
// xhr.responseText 表示作为响应体返回的文本
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4) {
    if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
      console.log("请求成功,响应数据:>>", xhr.responseText);
    }
  }
};

HTTP 头部

默认 xhr 会发送以下头部:

  • Accept:浏览器可以处理的内容类型,如:*/*
  • Accept-Charset:浏览器可以显示的字符集
  • Accept-Encoding:浏览器可以显示的字符集,如:gzip, deflate, br
  • Accept-Language:浏览器使用的语言,如:zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6
  • Connection:浏览器与服务器的连接类型,keep-alive
  • Cookie:页面中设置的 Cookie
  • Host:发送请求的页面所在的域,localhost:3000
  • Referer:发送求情的页面的 URI,拼错了(将错就错,正确是 Referrer),
  • User-Agent:浏览器的用户代理字符串。

设置自定义 HTTP 请求头
在 xhr.open()和 xhr.send()之间可以设置自定义请求头

xhr.setRequestHeader(headerName,value)

获取响应头

  • xhr.getResponseHeader(‘headername’),获取指定响应头
  • xhr.getAllResponseHeaders(),获取所有响应头,返回字符串

GET 请求

get 请求添加的查询字符串,每个 key 和 value 都必须经过 encodeURIComponent()编码,以&分隔

xhr.open("get", "example.php?name1=value1&name2=value2", true);

POST 请求

POST 请求主要是用于保存数据,因此必须设置 Content-Type,POST 请求的请求体可以包含很多种数据格式,

Content-Type 的值:

  • application/x-www-form-urlencoded,请求体数据就是提交表单时的数据格式,类似 name1=value1&name2=value2 这种格式的字符串
  • application/json,json 格式
// 1. 定义请求方法,请求url,是否是异步
xhr.open("post", url, true);
// post请求,必须设置请求体数据类型
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 2. 真正发送请求,send接收请求数据
xhr.send("name=dragon&age=18");

XMLHttpRequest level2

FormData 类型

FormData 类型便于表单序列化,也便于创建与表单类似格式的数据然后通过 XHR 发送,
不再需要给 XHR 对象显式设置任何请求头部了。XHR 对象能够识别作为 FormData 实例传入的数据类型并自动配置相应的头部。

const data = new FormData();

data.append("name", "dragon");
data.append("age", 10);

// 1. 定义请求方法,请求url,是否是异步
xhr.open("post", url, true);

// 2. 真正发送请求,send接收请求数据,可以是FormData
xhr.send(data);

超时

设置超时,触发超时事件

xhr.timeout = 1000; // 设置1 秒超时
xhr.ontimeout = function () {
  alert("Request did not return in a second.");
};

进度事件

  • loadstart,接收到响应第一个字节时
  • progress,接收期间
  • error,错误
  • abort,调用 xhr.abort()
  • load,成功接受完响应时
  • loadend,通信完成,且在 error,abort 或 load 之后触发
    每次请求都会首先触发 loadstart 事件,之后是一个或多个 progress 事件,接着是 error、abort=或 load 中的一个,最后以 loadend 事件结束。

跨域资源共享

跨源资源共享(CORS,Cross-Origin Resource Sharing)定义了浏览器与服务器如何实现跨源通信。CORS 背后的基本思路就是使用自定义的 HTTP 头部允许浏览器和服务器相互了解,以确实请求或响应应该成功还是失败。

预检请求

简单请求:

GET,简单的 POST,HEAD

简单的 post 请求指 content-type 为以下这些值的

  • text/plain
  • multipart/form-data
  • application/x-www-form-urlencoded

跨域技术

不依赖 CORS,也可以通过能够执行跨域请求的 DOM 特性来实现 AJAX

图片探测

图片探测是利用标签实现跨域通信的最早的一种技术.设置 onload 和 onerror 事件.
图片探测频繁用于跟踪用户在页面上的点击操作或动态显示广告。当然,图片探测的缺点是只能发
送 GET 请求和无法获取服务器响应的内容。这也是只能利用图片探测实现浏览器与服务器单向通信的
原因。

let img = new Image();

img.onload = function () {
  alert("done");
};

img.onerror = function () {
  alert("done");
};
img.src = "http://localhost:3000/";

JSONP

JSONP 调用是通过动态创建 script 元素并为 src 属性指定跨域 URL 实现的。实际上就是通过 script 拉取服务端代码.缺点是不安全,且无法确定是否请求失败,很少使用了.

JSONP 是“JSON with padding”的简写,是在 Web 服务上流行的一种 JSON 变体。JSONP 看起来 跟 JSON 一样,只是会被包在一个函数调用里.

JSONP 格式包含两个部分:回调和数据.

callback({ “name”: “Nicholas” });

客户端代码:

// 客户端定义一个函数,接收响应数据
function handleJSONP(res) {
  console.log(res);
}

const script = document.createElement("script");

// 客户端与后端约定以callback的值确定调用的函数名称
script.src = "http://localhost:3000?callback=handleJSONP";

document.body.insertBefore(script, document.body.firstChild);

服务端代码:

router.get("/", async (ctx) => {
  // 通过callback获取回调函数名称
  const cbName = ctx.query.callback;
  const data = "hello world";
  //返回格式 callback(data)
  ctx.body = `${cbName}(${JSON.stringify(data)})`;
});

Fetch API

能替代 XHR 对象,并且能在 Web Woker 中使用,必须是异步

基本用法

fetch()方法暴露在全局作用域,包括主页面执行线程,模块和工作线程

1. 发送请求
fetch(url),默认是 get 请求,返回一个 promise,该 promise 会 resovle 一个 response 对象,可通过它取得响应的资源

fetch(url).then((response) => {
  console.log(response); //response.status 200 状态码,response.body是一个ReadableStream,响应内容
  // 调用response.text方法,返回一个promise,resolve响应的数据
  response.text().then((data) => {
    console.log(data); //
  });
});

2. 读取响应
response 对象通过以下方法获取响应数据,均返回 resolve 数据的 Promise

  • response.text(),返回文本
  • response.blob(),返回不确定格式的二进制数据
  • response.arrayBuffer(),返回二进制数据
  • response.json(),返回 json
fetch(url).then((response) => {
  response.text().then((data) => {
    console.log(data);
  });
});

3. 处理状态码和请求失败
fetch API 可以通过 Response 对象的stauts(状态码)和statusText(状态文本),检查响应状态

响应状态
以下的状态 fetch()返回的 promise 还是会被 resolve

属性成功资源不存在服务器错误
response.stauts200404500
response.statusTextOKNot FoundInternal Server Error
response.oktruefalsefalse

重定向时的 response
会发生两次网络请求,最后 response.stauts 是 200,且 response.redirected 是 true

fetch()返回的 promise 被 reject 的情况
跨域,无网络链接,HTTPS 错配或者超时等情况导致 promise 被 reject 拒绝

fetch(url)
  .then((response) => {
    console.log(response);
  })
  .catch((e) => {
    // 捕获reject错误
    console.log(e);
  });

4. 自定义选项
fetch(url,init)

init 对象的键值如下:

body请求体,Blob,FormData,String,BufferSource,URLSearchParams,ReadableStream
method请求方法,GET,POST,PUT,DELELTE,OPTION,HEAD,默认是 GET
headers请求头,值是 Header 对象或者常规的对象,默认是不含键值对的 Header 对象,但浏览器仍然会发送一些头部
cache用于控制浏览器与 HTTP 缓存的交互,默认是 Default
credentials用于指定外发请求时如何包含 cookie,默认是 same-origin,同源时才发送 cookie
keepalive用于指示浏览器允许请求存在时间超出页面生命周期,默认是 false
mode请求模式,用于指示跨域请求的响应是否有效,以及客户端可以读取多少响应,默认是 cors,允许跨域
signal值是 AbortSignal 的实例,用于支持中断请求

常见 Fetch 请求模式

1. 发送 JSON 数据
JSON.stringify,POST,application/json

const headers = new Headers({
  "Content-Type": "application/json", // post请求,必须设置该Content-Type
});

fetch(url, {
  method: "POST", //指定请求HTTP方法
  body: JSON.stringify({ foo: "bar" }), // 在浏览器中的payload,是Request Payload类型
  headers,
}).then((response) => {
  console.log(response);
});

2. 在请求体中发送参数
可以发送任意参数,application/x-www-form-urlencoded,name1=value1&name2=value2

const headers = new Headers({
  "Content-Type": "application/x-www-form-urlencoded", // post请求,必须设置该Content-Type
});

fetch(url, {
  method: "POST",
  body: "foo=bar&baz=qux", //在浏览器的开发者工具中,看到的是FormData类型,因为是模拟表单提交
  headers,
}).then((response) => {
  console.log(response);
  response.json().then(console.log); //{foo: 'bar', baz: 'qux'}
});

3. 发送文件

const url = "/post";

const data = new FormData();

data.append("image", File对象1);
data.append("image", File对象2);

fetch(url, {
  method: "POST",
  body: data, // 如果是FormData,默认自动会设置Content-Type,
}).then((response) => {
  console.log(response);
  response.json().then(console.log); //{foo: 'bar', baz: 'qux'}
});

4. 加载 Blob 文件

const url = "/img.jpg";

fetch(url).then((response) => {
  console.log(response);
  response.blob().then((blob) => {
    // 根据blob创建objectURL
    const url = window.URL.createObjectURL(blob);

    const img = document.createElement("img");

    img.src = url;

    document.body.appendChild(img);
  });
});

5. 发送跨源请求
从不同的源请求资源,响应要包含 CORS 头部才能保证浏览器收到响应.

6. 中断请求
Fetch API 支持通过 AbortController/AbortSignal 对中断请求。调用 AbortController.
abort()会中断所有网络传输,特别适合希望停止传输大型负载的情况。中断进行中的 fetch()请求会
导致包含错误的拒绝。

在 fetch()第二个参数设置 signal,调用 AbortController.abort()

const url = "/img.jpg";

const abortController = new AbortController();

//fetch()第二个参数设置 signal为abortController.signal
fetch(url, { signal: abortController.signal }).catch((e) => {
  console.log("中断了请求"); //中断了请求
});

// 0ms后中断请求
setTimeout(() => {
  abortController.abort();
}, 0);

Headers 对象

Headers 对象存在 request,response 实例上,也可以通过 new Headers()创建

1.Headers 与 Map 的相似之处
和 Map 类型相似,具有 get(),set(),has(),delete()等实例方法,可以被迭代,有 keys(),values(),entries()接口

2. Headers 独有特性
可以通过键值对形式的对象初始化,append 同个键,会以","的分隔符拼接多个值

3. 头部护卫
某些 HTTP 头部不可以被客户端修改

Request 对象

1. 创建 Request 对象

new Request(url),new Request(url,init)

2. 克隆 Request 对象
前提: bodyUsed属性不为true,否则无论哪种方式是克隆不成功的

  • 通过new Request(request)
  • 通过request.clone()

request对象如果被new Request()或者fetch()使用过,那么就reqeust.bodyUsed就变为true,不能再被fetch()使用

let r1 = new Request("http://localhost:3000", {
  method: "POST",
  body: JSON.stringify({ name: "dragon" }),
});

let r2 = new Request(r1);

console.log(r1.bodyUsed); // true,由于使用了new Request(r1),r1.bodyUsed就会被标记为ture,
console.log(r2.bodyUsed); // false

fetch(r2).then((response) => {
  console.log(r2.bodyUsed); //true
});

建议通过clone方法,只有request.bodyUsed为false的情况下才能克隆成功,否则报错

let r = new Request("http://localhost:3000", {
  method: "POST",
  body: JSON.stringify({ name: "dragon" }),
});

r.clone(); //不会报错

r.text(); // 调用text(),bodyUsed变为true,再次克隆则会报错

r.clone(); // 报错

new Request(r1); // 报错
  1. 在fetch()中使用request对象

fetch(request,init),也可以传Request对象,并且再传init对象覆盖原配置.有请求体的Request只能在fetch()中使用一次,调用后request的bodyUsed变为true,如果想基于同一个请求体发送多次请求,在请求前先clone以下

let r = new Request("http://localhost:3000", {
  method: "POST",
  body: "foobar",
});

console.log(r.bodyUsed); //false
fetch(r);
console.log(r.bodyUsed); //true

fetch(r); //报错,TypeError: Failed to execute 'fetch' on 'Window': Cannot construct a Request with a Request object that has already been used.


// 先克隆再请求
// 3 个都会成功
fetch(r.clone());
fetch(r.clone());
fetch(r);

Response 对象

  1. 创建Response对象
    new Response(body?,init?)
let r = new Response();

let r2 = new Response(JSON.stringify({ foo: "bar" }), {
  status: 200,
  statusText: "OK",
});
console.log(r2);
//body: (...)
//bodyUsed: true
//headers: Headers {}
//ok: true
//redirected: false
//status: 200
//statusText: "OK"
//type: "default"
//url: ""
r2.text().then(console.log);

大多数情况下,都是通过fetch()返回的promise来resolve一个response对象,另外还有两个静态方法Response.redirect(),Response.error()

2. 读取响应状态
Response 对象包含一组只读属性,描述了请求完成后的状态.

属性
headers响应包含的Headers 对象
ok布尔值,表示HTTP 状态码的含义。200~299 的状态码返回true,其他状态码返回false
redirected布尔值,表示响应是否至少经过一次重定向
status整数,表示响应的HTTP 状态码
statusText字符串,包含对HTTP 状态码的正式描述.这个字段可能是空字符串
typebasic:表示标准的同源响应 cors:表示标准的跨源响应
url包含响应URL 的字符串。对于重定向响应,这是最终的URL

3. 克隆Response对象
克隆Response 对象的主要方式是使用clone()方法,这个方法会创建一个一模一样的副本,不会覆盖任何值.

let r1 = new Response("foo bar");

let r2 = r1.clone();

r1.text();

r1.clone(); // 报错,响应体已使用,bodyUsed为true的响应对象不能再次克隆

r1.text(); // 报错了, 带有响应体的响应对象只能读取一次,再次读取会报错

//想多次读取,则先克隆
r2.clone().arrayBuffer().then(console.log);
r2.clone().text().then(console.log);
r2.clone().blob().then(console.log);

Request,Response 及 Body 混入

Request 和Response 都使用了Fetch API 的Body 混入,提供了只读的body 属性(实现为ReadableStream)、只读的bodyUsed 布尔值(表示body 流是否已读)和一组方法

  • text()
  • json()
  • formData()
  • arrayBuffer()
  • blob()
response.text().then(text=>{})
response.json().then(json=>{})
response.formData().then(formData=>{})
response.arrayBuffer().then(arrayBuffer=>{})
response.blob().then(blob=>{})

Beacon API

普通的XHR对象或者fecth请求会在页面unload卸载后就中断,而navigator.sendBeacon()就不会,这个简单的方法接收一个URL 和一个数据有效载荷参数,并会发送一个POST请求。可选的数据有效载荷参数有ArrayBufferView、Blob、DOMString、FormData 实例。如果请求成功进入了最终要发送的任务队列,则这个方法返回true,否则返回false.

特点:

  • 在页面的生命周期的任何时候都能使用
  • 浏览器关闭也能使用
  • 会携带cookie
  • 状态码,超时或其他原因造成失败是不透明的,不能通过编程方式处理.
let isSended = navigator.sendBeacon(
  "http://localhost:3000",
  JSON.stringify({ foo: "bar" })
);

WebSocket

Web Socket(套接字)的目标是通过一个长时连接实现与服务器全双工、双向的通
API
let ws = new WebSocket(“ws://localhost:3000”);

  • ws.readyState,连接状态
  • ws.close(),关闭连接
  • ws.onmessage,监听消息
  • ws.onopen,在连接成功建立时触发
  • ws.onclose,在连接关闭时触发
  • ws.onerror在发生错误时触发。连接无法存续
  • ws.send(),参数可以是字符串,ArrayBuffer,或Blob
// 创建WebSocket实例,参数ws开头的url
let ws = new WebSocket("ws://localhost:3000");

ws.onmessage = function (e) {
  // 服务器推送的数据,data可能是blob或arrayBuffer
  console.log(e.data);
};

ws.onopen = function () {
  ws.send("我是客户端的socket消息");
  console.log(ws.readyState);
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值