Ajax 和 http | 青训营

前言

本文章主要涉及笔者对 HTTP 协议和 Ajax 请求的学习。

Ajax 请求

Ajax 是 Jesse James Garrett 提出的新术语:Asynchronous JavaScript and XML,意思是用 JavaScript 执行异步网络请求。

网络请求的发展

网络请求,是用来从服务端获取需要的信息,然后解析协议和内容,来进行页面渲染或者是信息获取的过程。

在很久以前,我们的网络请求除了静态资源(html/css/js等)文件的获取,主要用于表单的提交。我们在完成表单内容的填写之后,点击Submit按钮,表单开始提交,浏览器就会刷新页面,然后在新页面里告诉你操作是成功了还是失败了。

然后随着时间发展,大家觉得这样每次都刷新页面的体验太糟了,然后开始使用XMLHttpRequest来获取请求内容,再更新到页面中。页面开始支持局部更新、动态加载,后面还有懒加载、首屏加载等等,其实都可以算是基于这个基础吧。

同步请求会阻塞进程,页面呈现假死状态,导致体验效果也较差。接下来,Ajax 的应用越来越广,慢慢大家都开始使用异步请求 + 回调的方式,来进行请求处理。那是一个浏览器兼容困难时期,jQuery 封装的$.ajax(),由于兼容性处理较好,也开始被大家广泛使用 。

XMLHttpRequest

XMLHttpRequest让发送一个 HTTP 请求变得非常容易。你只需要简单的创建一个请求对象实例,打开一个 URL,然后发送这个请求。当传输完毕后,结果的 HTTP 状态以及返回的响应内容也可以从请求对象中获取。

来看个简单的例子(我们常用的 Ajax 处理):

js 复制代码
var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象

request.onreadystatechange = function() {
  // 状态发生变化时,函数被回调
  if (request.readyState == 4) {
    // 成功完成
    // 判断响应结果:
    if (request.status == 200) {
      // 成功,通过responseText拿到响应的文本
      console.log(request.responseText);
    } else {
      // 失败,根据响应码判断失败原因:
      console.log(request.status);
    }
  }
};

// 发送请求
// open的参数:
// 一:请求方法,包括get/post等
// 二:请求地址
// 三:表示是否异步请求,若为false则是同步请求,会阻塞进程
request.open("GET", "/api/categories", true);
request.send();

大概就是上面这样,来处理一个 HTTP 请求。我们通常会将它封装成一个通用的方法,方便调用。上面例子中使用200来判断是否成功,但有些时候200-400(不包括400)的范围,都可以算是成功的。

如果说我们将其封装起来,同时使用 ES6 的 Promise 的方式来操作的话,大概会是这样:

js 复制代码
function ajax({ method, url, params, contentType }) {
  const xhr = new XMLHttpRequest();
  const formData = new FormData();
  Object.keys(params).forEach(key => {
    formData.append(key, params[key]);
  });
  return new Promise((resolve, reject) => {
    try {
      xhr.open(method, url, false);
      xhr.setRequestHeader("Content-Type", contentType);
      xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 400) {
            // 这里我们使用200-400来判断
            resolve(xhr.responseText);
          } else {
            // 返回请求信息
            reject(xhr);
          }
        }
      };
      xhr.send(formData);
    } catch (err) {
      reject(err);
    }
  });
}

这里使用了FormData来处理。通过FormData对象可以组装一组用XMLHttpRequest发送请求的键/值对。

它可以更灵活方便的发送表单数据,因为可以独立于表单使用。如果你把表单的编码类型设置为multipart/form-data,则通过FormData传输的数据格式和表单通过submit()方法传输的数据格式相同。也支持文件的上传和添加。

HTTP 协议

理解 HTTP 协议

HTTP 结构

HTTP 消息的结构

  1. Request
cmd 复制代码
------------------
Request line
(包括:请求方法、请求的资源、HTTP协议的版本号)
------------------
Request header
(包括:Cache头域、Client头域、Cookie/Login头域、Entity头域、Miscellaneous头域、Transport头域等)
------------------
空行
------------------
Request body
------------------
  1. Response
cmd 复制代码
------------------
Response line
(包括:HTTP协议的版本号、状态码、消息)
------------------
Response header
(包括:Cache头域、Cookie/Login头域、Entity头域、Miscellaneous头域、Transport头域、Location头域等)
------------------
空行
------------------
Response body
------------------

状态码

状态码由三位数字组成,第一个数字定义了响应的类别(括号中为常见的状态码):

  • 1XX--提示信息:表示请求已被成功接收,继续处理
  • 2XX--成功:表示请求已被成功接收,理解,接受(200 OK)
  • 3XX--重定向:要完成请求必须进行更进一步的处理(302 Found 重定向/304 Not Modified 缓存)
  • 4XX--客户端错误:请求有语法错误或请求无法实现(400 Bad Request 客户端请求与语法错误/403 Forbidden 服务器拒绝提供服务/404 Not Found 请求资源不存在)
  • 5XX--服务器端错误:服务器未能实现合法的请求(500 Internal Server Error 服务器发生了不可预期的错误)

无连接的 HTTP

无连接

无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。

Keep-Alive

Keep-Alive 功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive 功能避免了建立或者重新建立连接。

无状态的 HTTP

无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。

通常我们会根据场景,使用CookieTokenSession等方法来记录用户状态,完善上下请求的承接性。

HTTP 与浏览器缓存

浏览器会在第一次请求完服务器后得到响应,我们可以在服务器中设置这些响应,从而达到在以后的请求中尽量减少甚至不从服务器获取资源的目的。

静态资源的缓存能减轻很多流量,如今我们的文件很多都加上了 md5,则缓存的使用越来越广泛。

浏览器是依靠请求和响应中的的头信息来控制缓存的,主要涉及ExpiresCache-ControlLast-Modified/If-Modified-SinceETag/If-None-Match这几个。

第一次请求:

再次请求:

HTTP 与跨域

浏览器同源政策

同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。所谓"同源"指的是"三个相同": 协议相同、域名相同、端口相同。

随着互联网的发展,"同源政策"越来越严格。目前,如果非同源,共有三种行为受到限制。

  1. CookieLocalStorageIndexDB无法读取。
  2. DOM无法获得。
  3. AJAX请求不能发送。

前端解决跨域

跨域方法大概以下几种:

  • document.domain + iframe(只有在主域相同的时候才能使用该方法)
  • 动态创建script(JSONP)
  • location.hash + iframe
  • window.name + iframe
  • postMessage
  • CORS
  • websockets

现在的话,应该是 CORS 的使用会更广泛吧。实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

请求联调

一般来说,我们的日常联调通常有两种:浏览器查看请求,或是工具抓包查看(Fiddler)。

浏览器查看请求

我们又来看浏览器的控制台了:

Network 面板

Network 面板可以记录页面上的网络请求的详情信息,从发起网页页面请求 Request 后分析 HTTP 请求后得到的各个请求资源信息(包括状态、资源类型、大小、所用时间、Request 和 Response 等),可以根据这个进行网络性能优化。

该面板主要包括 5 大块窗格,如图:

  • Controls:控制 Network 的外观和功能。
  • Filters:控制 Requests Table 具体显示哪些内容。
  • Overview:显示获取到资源的时间轴信息。
  • Requests Table:按资源获取的前后顺序显示所有获取到的资源信息,点击资源名可以查看该资源的详细信息。
  • Summary:显示总的请求数、数据传输量、加载时间信息。

查看具体资源的详情

通过点击某个资源的 Name 可以查看该资源的详细信息,根据选择的资源类型显示的信息也不太一样,可能包括如下 Tab 信息:

  • Headers:该资源的 HTTP 头信息。
  • Preview:根据你所选择的资源类型(JSON、图片、文本)显示相应的预览。
  • Response:显示 HTTP 的 Response 信息。
  • Cookies:显示资源 HTTP 的 Request 和 Response 过程中的 Cookies 信息。
  • Timing:显示资源在整个请求生命周期过程中各部分花费的时间。

一般来说,联调主要关注请求是否正确发送、回包是否是约定的格式,所以我们更多使用资源详情的查看,包括:

  • 查看 HTTP 头信息是否正确
  • 查看请求数据是否带上
  • 查看请求是否成功,分析 HTTP 状态码
  • 查看回包格式和内容是否正确

Fiddler

Fiddler 是一个 HTTP 的调试代理,以代理服务器的方式,监听系统的 Http 网络数据流动。Fiddler 可以也可以让你检查所有的 HTTP 通讯,设置断点,以及 Fiddle 所有的"进出"的数据(可用于抓包、修改请求等)。

通常来说,我们会使用它来解决一些问题:

  1. 查看请求详情(类似上方的浏览器 Network 面板)。
  2. 请求失败时,抓包给后台查看问题。
  3. 模拟请求。
  4. 拦截请求,并更改请求内容。
  5. 移动端的请求查看和抓包。

结束语

这一节主要讲了 HTTP 请求相关,包括 Ajax(XMLHttpRequest)、HTTP 协议/跨域/缓存等,以及常用的前后台交互(联调)方式的介绍。这里面都是书面的介绍,我们需要更多的其实是实践,动手去写吧。

相关推荐
Find24 天前
MaxKB 集成langchain + Vue + PostgreSQL 的 本地大模型+本地知识库 构建私有大模型 | MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 14.数组元素之和最小化 | 豆包MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 25.DNA序列编辑距离 | 豆包MarsCode AI刷题
青训营笔记
理tan王子24 天前
伴学笔记 AI刷题 9.超市里的货物架调整 | 豆包MarsCode AI刷题
青训营笔记
夭要7夜宵1 个月前
分而治之,主题分片Partition | 豆包MarsCode AI刷题
青训营笔记
三六1 个月前
刷题漫漫路(二)| 豆包MarsCode AI刷题
青训营笔记
tabzzz1 个月前
突破Zustand的局限性:与React ContentAPI搭配使用
前端·青训营笔记
Serendipity5651 个月前
Go 语言入门指南——单元测试 | 豆包MarsCode AI刷题;
青训营笔记
wml1 个月前
前端实践-使用React实现简单代办事项列表 | 豆包MarsCode AI刷题
青训营笔记
用户44710308932421 个月前
详解前端框架中的设计模式 | 豆包MarsCode AI刷题
青训营笔记