JavaScript基础回顾(八):网络请求

3 阅读7分钟

HTTP/HTTPS

HTTP(超文本传输协议),是一种应用层协议,主要用于网络上客户端与服务器的通信,以获取和传输超文本数据。HTTP 通常运行在 TCP 之上。TCP(传输控制协议)是传输层协议,通过三次握手来建立两端可靠的连接。

HTTPS(安全超文本传输协议),是在 HTTP 基础上加入了 SSL/TLS 协议,提供了数据加密、安全性验证和身份验证,安全性更高。

简单罗列一下 http 的关键内容:

  • http 方法:get/post/put/delete/patch/head/options。
  • 状态码:2xx 成功,3xx 重定向,4xx 客户端错误,5xx 服务器错误。
  • http 头:Authorization 等请求头,Content-Type 等响应头。
  • 内容编码:服务器可以通过 gzip 等内容编码压缩响应数据。
  • 跨站资源共享(CORS):通过 Access-Control-Allow-Origin 限制可访问名单。
  • 缓存控制:Cache-Control 允许服务器控制资源的缓存行为。
  • 连接管理:HTTP/1.1 默认使用持续连接(Connection: keep-alive)。HTTP/2 引入了多路复用。
  • 分快传输:http 可以使用分块传输编码,不需要服务器知道内容长度。

XMLHttpRequest

2005 年,Ajax 技术被专门提出,这项技术能够实现在不刷新浏览器的情况下,实现服务器请求。其关键技术是 XMLHttpRequest 对象,XMLHttpRequest 对象比 Ajax 的历史更长。

这是 XMLHttpRequest 的一个基本使用示例:

let xhr = new XMLHttpRequest();
xhr.onload = function (event) {
  if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304) {
    alert(xhr.responseText);
  } else {
    alert("Request was unsuccessful: " + xhr.status);
  }
};
xhr.onprogress = function (event) {
  let divStatus = document.getElementById("status");
  if (event.lengthComputable) {
    divStatus.innerHTML =
      "Received " + event.position + " of " + event.totalSize + " bytes";
  }
};
xhr.timeout = 1000; // 设置 1 秒超时
xhr.ontimeout = function () {
  alert("Request did not return in a second.");
};
xhr.onerror = function() {
  console.log('An error occurred during the request.');
};
xhr.open("get", "altevents.php", true);
let form = document.getElementById("user-info");
xhr.send(new FormData(form));

encodeURICompanent 与 DecodeURI

encodeURICompanent

encodeURIComponent 是 JavaScript 中的一个函数,用于编码一个 URI 的组成部分。在 URI 中,某些符号具有特殊意义,如冒号:、正斜杠/、问号?、井号#、空格等,如果直接将这些字符插入到 URI 中,会导致 URI 无法解析。encodeURIComponent 会将字符串中所有非字母数组字符(除了-、_、.、~等)都转为百分号编码。

一个为 get 请求添加参数的方法,基于 encodeURIComponent() 方法封装:

function addURLParam(url, name, value) {
  url += url.indexOf("?") == -1 ? "?" : "&";
  url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
  return url;
}

DecodeURI

decodeURI 则是对整个 URI 进行编码,并且只编码影响 URI 结构的字符。

使用场景

  • 构建查询参数与请求数据:确保服务器正确解析请求。
  • 避免 XSS 攻击:编码后的特殊字符不会被浏览器作为代码执行。
  • 处理 URL 国际化:确保 URL 在各种环境下正常处理和显示。
  • 分享链接:分享的连接不会因为特殊字符导致错误。

URI 与 URL

URI(Uniform Resource Identifier)和URL(Uniform Resource Locator)是互联网上用于标识资源的两种不同的概念,它们之间有着密切的关系,但也有一些区别:

  1. URI(统一资源标识符):
    • URI是一种标准化的字符串,用于标识资源。资源可以是任何东西,比如文档、图片、视频、服务等。
    • URI包括URL和URN(Uniform Resource Name)两种形式。
    • URI的一般格式为:scheme:[//[user:password@]host[:port]][/path][?query][#fragment]
    • 例如,http://www.example.com/index.html 是一个URI,其中http是scheme,www.example.com是host,index.html是path。
  1. URL(统一资源定位符):
    • URL是URI的一种,专门用于标识资源的位置,即资源在网络中的地址。
    • URL提供了一种访问资源的方式,通过指定协议、服务器地址、路径等信息,可以直接访问到资源。
    • URL是互联网上最常见的URI类型,大多数时候,当我们提到URI时,实际上指的是URL。
    • 例如,http://www.example.com/index.html 就是一个URL,它通过HTTP协议定位到了www.example.com服务器上的index.html文件。
  1. URN(统一资源名称):
    • URN是URI的另一种形式,它通过名称来标识资源,而不是通过位置。URN的设计目的是提供一种持久的、位置无关的资源标识方式。
    • URN的格式通常为:urn:namespace:specific,其中namespace是命名空间的标识,specific是该命名空间下的具体名称。
    • 例如,urn:isbn:0-395-36341-1 是一个URN,它标识了一个特定的ISBN号。
  1. 区别:
    • URI是一个更广泛的概念,包括了URL和URN。
    • URL是URI的一种,专门用于标识资源的位置。
    • URN是URI的另一种形式,通过名称而不是位置来标识资源。

在实际应用中,URL是最常用的URI类型,因为它提供了一种直接访问资源的方式。URN虽然理论上提供了一种更持久的资源标识方式,但在实际应用中使用较少。

跨站资源共享

  • CORS(cross-origin resource sharing)定义了浏览器与服务器如何实现跨源通信。请求头携带 Origin 参数,指请求发送的页面源;浏览器如果响应了,会在头部携带 Access-Control-Allow-Origin,这个参数可以是具体源,也可以是*,代表公开。
  • 预检请求 options:当浏览器使用自定义头部(如Authorization、Content-Type: application/json)、非简单请求(不是 HEAD、GET、POST)或请求中使用了 XMLHttpRequestUpload 对象时,会触发预检请求。
    • 请求内容:Origin:与简单请求相同;Access-Control-Request-Method:请求希望使用的方法;Access-Control-Request-Headers:(可选)要使用的逗号分隔的自定义头部列表。
    • 响应内容:Access-Control-Allow-Origin:与简单请求相同;Access-Control-Allow-Methods:允许的方法(逗号分隔的列表);Access-Control-Allow-Headers:服务器允许的头部(逗号分隔的列表);Access-Control-Max-Age:缓存预检请求的秒数。
  • 跨域请求通过 withCredentials: true 来携带凭证(cookie、HTTP 认证信息、SSL 证书等)。

Fetch

  • fetch 失败(触发 catch)的情况:请求发起错误(如违反 CORS、无网络、HTTPS 错配等)、超时。
  • 其他情况都是通过 then 来处理,通过 status 来具体判断 200、404、500 等。

fetch 参数(与 Request 对象基本相同):

fetch({
  body, // 指定使用请求体时请求体的内容
  cache, // 控制浏览器与HTTP缓存的交互
  credentials, // 用于指定在外发请求中如何包含 cookie
  headers, // 用于指定请求头部,必须是 Headers 实例
  integrity, // 用于强制子资源完整性
  keepalive, // 用于指示浏览器允许请求存在时间超出页面生命周期
  method, // 用于指定 HTTP 请求方法
  mode, // 用于指定请求模式,cors 等
  redirect, // 用于指定如何处理重定向响应(状态码为 301、302、303、307 或 308)
  referrer, // 用于指定 HTTP 的 Referer 头部的内容
  referrerPolicy, // 用于指定 HTTP 的 Referer 头部
  signal, // 用于支持通过 AbortController 中断进行中的 fetch()请求
  url, // 请求地址
})

Response 对象:

Response {
  body: (...), // text/json/formData/arrayBuffer/blob
  bodyUsed: false,
  headers: Headers {}, // 响应包含的 Headers 对象
  ok: true, // 布尔值,表示 HTTP 状态码的含义: 200-299
  redirected: false, // 布尔值,表示响应是否至少经过一次重定向
  status: 200, // 整数,表示响应的 HTTP 状态码
  statusText: "OK", // 字符串,包含对 HTTP 状态码的正式描述
  type: "default", // 字符串,包含响应类型: basic/cors/error等
  url: "", // 包含响应 URL 的字符串
}

Web Socket

Web Socket(套接字)的目标是通过一个长时连接实现与服务器全双工、双向的通信。在 JavaScript 中创建 Web Socket 时,一个 HTTP 请求会发送到服务器以初始化连接。Web Socket 要使用 ws://和 wss:// 协议。

let socket = new WebSocket("ws://www.example.com/server.php");
socket.onopen = function () {
  alert("Connection established.");
};
socket.onerror = function () {
  alert("Connection error.");
};
socket.onclose = function () {
  alert("Connection closed.");
};

let stringData = "Hello world!";
let arrayBufferData = Uint8Array.from(["f", "o", "o"]);
let blobData = new Blob(["f", "o", "o"]);
socket.send(stringData);
socket.send(arrayBufferData.buffer);
socket.send(blobData);

socket.onmessage = function (event) {
  let data = event.data;
  // 对数据执行某些操作
};

前端需关注的网络安全问题

敏感信息泄露

  • 通过 httpOnly 等禁止 JavaScript 访问 cookie。
  • 明文隐私数据进行脱敏处理。
  • 使用 HTTPS 代替 HTTP,确保数据传输过程中的加密。

XSS 攻击(跨站脚本攻击)

  • 对用户输入进行适当的过滤和转义,确保在输出到 HTML 页面时不会被作为代码执行。
  • 使用内容安全策略(CSP)来限制加载和执行的资源,减少 XSS 攻击的风险。
  • 验证上传文件的安全。

CSRF(跨站请求伪造)

  • 使用 CSRF token,并在服务器验证,确认请求来自用户。
  • 验证 HTTP referer 字段,确保请求来自合法来源。
  • 设置合理的 cookie sameSite 属性,限制第三方 cookie 的使用。

CORS(跨站资源共享)滥用

  • 设置可访问白名单。

权限限制

  • 敏感资源需要添加授权及验证机制。