# AJAX 基础
TIP
结合 Postman 验证本章节内容。
# 1. 基本概念
传统 Web 应用的缺点:
独占式的请求
频繁的页面刷新
当下的前端开发通常都会借助 Ajax 技术,Ajax 并没有太多新的内容,但 Ajax 丰富了前端开发的功能。
Ajax 技术的核心概念就是两个:『异步』和『局部刷新』。
AJAX 的全称是 Asynchronous JavaScript XML(异步 JavaScript 和 XML),从 AJAX 的组合名称可以看出 AJAX 其实并不是一种技术,而是多种技术的组合,每种技术都有其独特之处,合在一起就成了功能强大的技术。AJAX 的出现揭开了无刷新页面的新时代。
利用 AJAX 技术,Web 前端只需要在后台与服务器进行少量数据交换。
AJAX 采用了异步交互的方式,从而改变了同步交互过程中的“请求 - 等待 - 请求 - 等待”的模式。
异步,是指基于 Ajax 的应用与服务器通信的方式。对于
- 传统的 Web 应用,每次用户发送请求,向服务器请求获得新数据时,浏览器都会完全丢弃当前页面,而等待重新加载新的页面。而在服务器完全响应之前,用户浏览器将一片空白,用户的动作必须中断。而
- 异步是指用户发送请求后,无须等待,请求在后台发送,不会阻塞用户当前活动。用户无须等待第一次请求得到完全响应,即可发送第二次请求。
简单来说,AJAX 的工作原理是通过 xmlHttpRequest 对象来向服务器发出异步请求。xmlHttprequest 可以同步或异步返回 Web 服务器的响应,并且能以文本或一个 DOM 文档形式返回内容。
普通的 Web 项目的工作流程是:
TIP
- 发起 <请求-1>
- 获得 <页面-1>
- 发起 <请求-2>
- 获得 <页面-2>
- 发起 <请求-3>
- 获得 <页面-3>
- ...
Ajax 的 Web 项目的流程是:
TIP
- 发起 <请求-1>
- 获得 <页面-1>
- 发起 <请求-2>
- 获得 数据,修改 <页面-1>
- 发起 <请求-3>
- 获得 数据,修改 <页面-1>
- ...
AJAX 的核心是 XMLHttpRequest 对象(首次出现于 IE5,如今已被 HTML5 制定为正式规范。)。XMLHttpRequest 提供了异步通信的能力,通过它浏览器可以向服务器发送异步的请求,也可通过它读取服务器响应。
JavaScript 主要完成 AJAX 如下事情:
创建 XMLHttpRequest 对象;
通过 XMLHttpRequest 对象向服务器发送请求;
创建回调函数,监视服务器响应状态,在服务器响应完成后,回调函数启动;
回调函数通过 DOM 动态更新 HTML 页面;
var request = new XMLHttpRequest(); // 创建XMLHttpRequest对象
// ajax 是异步的,设置回调函数
request.onreadystatechange = function () { // 状态发生变化时,函数被回调
if (request.readyState === 4) { // 成功完成
// 判断响应状态码
if (request.status === 200) {
// 成功,通过 responseText 拿到响应的文本:
return success(request.responseText);
} else {
// 失败,根据响应码判断失败原因:
return fail(request.status);
}
} else {
/* HTTP 请求还在继续...
0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
*/
}
}
// 发送请求:
request.open('GET', '/api/categories');
request.setRequestHeader("Content-Type", "application/json") //设置请求头
request.send(); //到这一步,请求才正式发出
# 2. HTTP 请求的 content-type
HTTP 请求的请求头中会有一个较为重要的键值对:content-type,它的值常见有 2 个:application/x-www-form-urlencoded 和 application/json 。
HTTP 请求的 content-type 决定了:本次请求所提交的参数(例如,登录请求所携带的用户名和密码信息)是 Query String 格式,还是 JSON String 格式。
警告
HTTP 请求提交的参数数据,无论是 Query String 格式,还是 JSON String 格式,都只是格式的不同,其数据内容是完全一致的。
就像同样一句话,(在不考虑同音字引起歧义的情况下)我用拼音写,还是用汉字写,本质上并没有区别,两种写法传递的信息,表达的含义是一样的,它们只是『写法不同』而已。
注意
再次强调一遍,上述的两种格式,显而易见,只是格式的不同。它俩表达的含义、传递的数据本质上是一样的!
# 3. HTTP 响应的 content-type
HTTP 响应的响应头中也有一个 content-type 键值对,它的值我们常见两种:
# | content-type |
---|---|
1 | text/html |
2 | application/json |
HTTP 响应头中的 content-type 决定了:(通过 Tomcat)回给浏览器的数据的格式类型:
如果,你(通过 Tomcat)回给浏览器的数据是一个 HTML 格式的字符串,那么,本次请 HTTP 响应的 content-type 的值就应该是
text/html
与之呼应;如果,你(通过 Tomcat)回给浏览器的数据是一个 JSON 格式的字符串,那么,本次请 HTTP 响应的 content-type 的值就应该是
application/json
与之呼应;
# 4. AJAX 请求和 content-type
当前浏览器发出的是否是 AJAX 请求与 content-type 无关,而是与是否使用了 JavaScript 的 XMLHttpRequest 有关。
所以一个常见的『标准错误答案』是:HTTP 请求头的 content-type 的值为 application/json ,则意味着当前请求是 AJAX 请求。这个说法是错的!
注意
“当前请求是一个 AJAX 请求,还是一个普通请求?当前请求是一个传递 query-string 参数的请求,还是一个传递 json-string 参数的请求?” 这是 2 个独立的问题!
就好像你去思考 “这个人是一个男人,还是一个女人?ta 是一个好人,还是一个坏人?” 一样。如果你直接回答:因为他是男人,所以他是个好人
。那么,很显然别人就会觉得你这个人脑子不好使。
正确的观点应该是:AJAX 请求只和 XMLHttpRequest 对象有关。通过 XMLHttpRequest 发出的请求就是 AJAX 请求,反之则不是。
# | 参数风格 | content-type | Servlet 获取参数 |
---|---|---|---|
普通请求一 | query-string 格式 | application/x-www-form-urlencoded | request.getParameter() |
普通请求二 | json-string 格式 | application/json | request.getReader() |
AJAX 请求一 | query-string 格式 | application/x-www-form-urlencoded | request.getParameter() |
AJAX 请求二 | json-string 格式 | application/json | request.getReader() |
其中,普通请求二
比较少见。
# 5. application/json 和 request.getReader()
由于 Servlet 中的 request.getParameter("...") 只对 content-type 值为 application/x-www-form-urlencoded 的情况有效有效。
当你的请求参数风格是 json-string 风格,即,HTTP 请求头的c ontent-type 值为 application/json 时,你的 Servlet 中的 request.getParameter("...") 方法的值为 null,是获取不到页面提交的参数的。
这种情况下,你需要自己“想办法”从 HTTP 请求的 Body 中,将请求参数取出来:
BufferedReader br = request.getReader();
String str, wholeStr = "";
while ((str = br.readLine()) != null) {
wholeStr += str;
}
System.out.println(wholeStr);
# 6. 人造『奇葩』请求
下面几种请求方式,在我们通过浏览器向后台发出 HTTP 请求时,是无法出现的,浏览器不会组装出这样的 HTTP 请求。它们都是我们通过 postman 这样的客户端工具(或其它方式)人为『造』出来的『奇葩』情况。
这些请求都是属于『看起来合乎 HTTP 规则要求,但是大家都不会这么用』的情况。
# 人造奇葩请求一
参数格式为 query-string 风格,但是 HTTP 请求头的 content-type 值为 application/json;
参数格式为 json-string 风格,但是 HTTP 请求头的 content-type 值为 application/x-www-form-urlencoded。
上面两种都是属于:我口头告诉你我给你的是个苹果,但是实际上我手里递给你的是个梨。
虽然看似没有报错,它俩都属于逻辑上的 bug,本不应该出现这样的情况。
# 人造“奇葩”请求二
发出 GET 请求,提交的参数是 json-string 风格,放在 HTTP 请求体中,HTTP 请求头中 content-type 值为 application/json 。
上诉请求的“奇葩”之处在于:
正常情况下,Get 请求的请求参数一般都是 query-string 风格,而不是 json-string;
正常情况下,Get 请求的请求参数都是拼接在 URL 后面,写在 HTTP 请求行中的,而不是在请求体中;
正常情况下,Get 请求由于请求体中无内容,它的 HTTP 请求头中是没有 content-type 键值对的。
相较于 奇葩请求一
而言,这样的请求不算太奇葩,因为,它不是一种错,只是少见。大家更习惯于 get 的参数以 query-string 的格式拼接在 URL 后面,放在 HTTP 请求头中,HTTP 请求体空着。
有可能你会遇到这种请求,它并非特别“奇葩”、罕见。
# 人造奇葩请求三
发出 POST 请求,提交的参数是 query-string,拼接在 URL 后面,放在 HTTP 请求行中。由于 HTTP 请求体中是空的,所有 HTTP 请求头中没有 content-type 键值对。
这种请求的奇葩之处在于:既然你想这么干,为什么不用 get 请求?
『完』