ajax、XHR和FormData

1,100 阅读8分钟

前言

在学习JS的过程中,渐渐意识到基础知识真的太重要了。在今天这篇文章中,我想站在前端的角度,跟大家聊一聊客户端浏览器和服务器之间的通信问题。这个问题涉及到两个概念,ajaxXHR。这篇文章除了会介绍这两个概念之外,我们还会聊一聊FormData相关的一些话题。

ajax

在维基百科中,关于ajax的定义是这样的:

AJAX,即"Asynchronous JavaScript and XML"(异步的JavaScript和XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。AJAX应用可以仅向服务器发送并取回必须的数据,并在客户端采用JavaScript处理来自服务器的回应。

我不知道大家伙儿是怎么想的,但是我在拿到这个概念之后,心里有一个疑问:这项技术和XML有什么关系呢?明明它是一项关于浏览器和服务器之间交互的解决方案。JavaScript还是可以理解的,毕竟在浏览器收到来自服务器的响应时会用它来进行数据处理。完全看不出来这关XML什么事啊,它名字里为什么会有个XML呢?这个问题的答案,我在另一个和他名字很像的技术中似乎找到了。那边是讲这些名字只是XML流行时的遗迹。简单来说,ajax就是使用XMLHttpRequest对象与服务器通信。到这里,我们的第二个主角也登场了。说到通信,无非就是发送和接受了。鉴于我们这里的通信是在网络环境下,利用HTTP协议进行的通信,那么ajax要做的事情自然呼之欲出了:

  • 在不重新加载页面的情况下发送请求给服务器
  • 接受并使用从服务器发来的数据

图片.png

如上图所示,在AJAX出现之前,传统的web应用都只能在提交表单时向服务器发送request请求,而服务器的响应也是新的HTML页面。最尴尬的问题是,新的HTML页面和原本的页面大部分都相同。这就使得这种方式极大的占用了带宽。

从这个角度来看,AJAX的异步性所带来的在不刷新页面的前提下维护数据就变得很喜人。

XHR

XMLHttpRequest(XHR)是所有浏览器都支持的对象,用于与服务器的交互,它定义了用脚本操纵HTTP的API。除了常用的GET请求,这个API还包含实现POST请求的能力,同时它能用文本或Document对象的形式返回服务器的响应。 XMLHttpRequest这个对象的继承关系如下:

图片.png

这样的继承关系表明了属于父类(EventTarget、XMLHttpRequestTarget)的方法和属性也可以被应用到子类(XMLHttpRequest)上。

1. 发送HTTP请求

利用XHR发送HTTP请求的第一步是创建一个新的XMLHttpRequest对象:

var xhr = new XMLHttpRequest()

第二步是利用这个对象的方法.open()方法和.send()方法打开一个HTTP请求和向服务器发送请求数据。

xhr.open("GET",url)
xhr.send()    

在这里,需要注意几个问题:

  1. HTTP请求的组成:
    • HTTP请求方法或动作:POST、GET这些参数
    • 正在请求的URL
    • 一个可选的请求头集合,可能包括身份验证信息
    • 一个可选的请求主体
  2. POST和GET的区别:
    • GET用于常规请求,它适用于当URL完全指定请求资源,当请求对服务器没有任何副作用以及当服务器的响应是可缓存时。GET请求绝对没有主体,所以在使用.send()方法时,应该传递null或者省略这个参数。
    • POST常用于提交表单。它在请求主体中包含额外数据(表单数据)且这些数据常存储到服务器上的数据库中(副作用)。相同URL的重复POST请求从服务器得到的响应可能不同,同时不应该缓存使用这个方法的请求。POST请求通常拥有主体,同时它应该匹配使用setRequestHeader()指定Content-Type头。
  3. .open()方法的参数:
    • method:要使用的HTTP方法
    • url: 发送请求的URL
    • async(可选): 一个可选的布尔参数,表示是否执行异步操作,默认为true。如果值为falsesend()方法直到收到答复前不会返回。如果true,已完成事务的通知可供事件监听器使用。
    • user(可选): 可选的用户名用于认证
    • password(可选): 可选的密码用于认证
  4. .send()方法:用于发送 HTTP 请求。如果是异步请求(默认为异步请求),则此方法会在请求发送后立即返回;如果是同步请求,则此方法直到响应到达后才会返回。XMLHttpRequest.send() 方法接受一个可选的参数,其作为请求主体;如果请求方法是 GET 或者 HEAD,则应将请求主体设置为 null。需要注意的是:发送HTTP请求时一定要调用这个方法
  5. 添加请求头:该方法时设置HTTP请求头部的方法。它必须在.open()send()之间调用。它的使用语法如下:
xhr.setRequestHeader(header, value);

2. 处理服务器响应

服务器返回的HTTP响应包含三部分:

  • 一个数字和文字组成的状态码,用来显示请求的成功和失败
  • 一个响应头集合
  • 响应主体 利用XMLHTTPResponse对于服务器返回的响应的处理就是对这三个部分的处理。由于在编码时不知道响应何时能够传回到客户端浏览器,需要一个标识来指示响应的阶段。这就是XMLHttpRequest的readyState属性。服务器返回的相应内容也是由XMLHttpRequest属性来存储的。与服务器响应相关的属性有:
  • readyState:readyState是一个整数,它指定了HTTP请求的状态。它的取值如下
    • 0:代表"UNSENT"状态,表示代理被创建,但是未调用open()方法
    • 1:代表"OPENED"状态,表示open()方法已经被调用
    • 2:代表"HEADERS_RECEIVED"状态,表示send()方法已经被调用,并且都不和状态已经可获得。
    • 3:代表"LOADING"状态,表示下载中,responseText已经获得部分数据
    • 4:代表"DONE"状态,表示下载操作已完成 理论上,每次readyState属性改变都会触发onreadyStateChange事件。在实际中,当readyState改编为0或1时可能没有触发这个时间。当调用send()时,即使readystate仍处于OPEN状态,也通常触发它。某些浏览器在LOADING状态时能触发多吃时间来给出进度反馈。当readyState值改变为4或服务器响应完成时,所有的浏览器都触onreadyStateChange事件。我们在处理服务器返回的响应数据时,需要监听onreadyStateChange事件,并且判断readyState的值,检验服务器相应是否完成。在完成之后,处理响应数据。

图片.png

xhr.onreadyStateChange = () => {
    if(xhr.readyState === 4){
    // 处理服务器返回的响应数据
    }
}
  • status:XMLHTTPRequest.status返回了XMLHttpRequset响应中的数字状态码,是一个只读属性。在请求完成之前,status的值为0。它是标准的HTTP状态码。

  • statusText:以文字的形式表示HTTP状态码。例如:200对应的statusText属性为'OK'

  • responseXML:该属性是一个只读值。它返回一个包含请求检索的HTML或XML的Document。如果请求未成功、尚未发送、或者检索的数据无法正确解析为XML或HTML,则为null。

  • responseText:以文本的形式返回的响应主体。当处理一个异步的request时,尽管当前请求并没有结束,responseText的返回值时当前从后端接收到的内容。当请求状态readyState变为XMLHttpRequest.DONE(4),且status值为200("OK")时,responseText是全部后端的返回数据。

最后,完整的处理后端服务器返回数据的代码如下:

xhr.onreadyStateChange = () => {
    if(xhr.readyState === 4){
        if(XPathResult.status === 200) {
        // 处理服务器返回的响应数据
        } else {    
        // 处理相应失败的结果
        }
    }
}

FormData

在发送HTTP的request请求的时候,有时候需要传递数据。当然,我们可以直接将传递的参数直接写到JavaScript对象里面。但是在提交表单的时候,一个一个获取数据,会比较麻烦。因此,FormData提供了一种表示表单数据的键值对key/value的构造方式,并且可以轻松的将数据通过XMLHttpRequest.send()发送出去。

构造函数

FormData()构造函数用于创建一个新的FormData对象。

var formData = new FormData(form)

其中,参数form是可选的,表示一个HTML上的<form>表单元素。当它被指定的时候,这种方式创建的FormData对象会自动将form中的表单值也包含进去,包括文件内容也会比编码之后包含进去。

方法