先来一个图,看看需要学习的内容
请求与响应原理
- Message , 消息/报文,在HTTP客户端和服务器之间传递的数据块。
- HTTP规定:消息必须符合特定的格式才能彼此理解(服务器厂商很多,需要统一标准)
- Requset Message:客户端向服务器发送的请求消息。
- Response Message:服务器根据客户端的请求消息,返回给客户端的响应消息。
HTTP消息结构概述
无论是Request Message还是 Response Message,每个消息都是由3部分组成:
- Start Line : 消息起始行,必须,基本描述;
- Header:消息头部/报头,0~N个,消息详细属性;
- Body:消息主体,可选,包含数据的主体;
HTTP协议的详细格式是由RFC文档确定的,其中规定了两种消息格式:
Request Message:请求消息/报文
Response Message:响应消息/报文
每个消息都分为3个部分:
1,起始行 CRLF(回车换行)
2,消息头部/报头(0~N个):
消息头部/报头 1CRLF
消息头部/报头 2CRLF
。。。
CRLF(空一行)3,消息正文/主体 CRLF
起始行和消息头是纯ASCII字符,不能有中文,这就决定了URL不能有中文,因为请求URL放在起始行的,起始行是不准有中文的。不能被地址栏里的中文迷惑了,看看下面的例子,故意在地址栏里面输入中文参数。
所以,我们要用encode,encodeURIComponent等函数,因为协议规定,起始行和消息头是不能有非ASCII字符的。
消息主体 是一个可选的数据块,其中的数据可以是空,或者是字符数据(HTML,CSS,JS等)或者二进制数据(图,音视频等字节数据)
请求方法:
GET: 指明客户端想从服务器获取指定的资源(输入你想获取的员工的编号);
POST:邮寄,指明客户端想发送给服务器一些数据(表单有很多项,需要服务器保存或者验证的);
PUT:指明客户端想让服务器保存某个资源,让服务器帮我把某个资源保存一下;
DELETE:指明客户端想让服务器删除某个资源;
HEAD:指明客户端只想查看指定资源的响应头部信息,而不要资源本身。
TRACE:客户端可以对请求消息的传输路径进行追踪,可以看到到底经过了哪些代理服务器;
下面看个例子
1.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
html {
font-size: 25px;
}
</style>
<script type="text/javascript" src="./1.js"></script>
</head>
<body>
<h3>1.html</h3>
<img src="./1.jpg">
<a href="2.html">超链接</a></br>
<button onclick="jump()">跳转</button>
</body>
</html>
1.js:
function jump() {
location.href = '2.html';
}
2.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<h3>2.html</h3>
</body>
</html>
先是输入1.html的地址:http://172.17.132.36/test/1.html
看图,全是GET请求。
以1.html为例,GET方式,并没有请求体,消息头完了就完了,既然没有请求体,所以也就没有那个空行。看看请求报头,共8条,每条后面都是一个换行。
在1.html中点击“超链接”蹦到2.html了:发起者是other,完全等同于在地址栏里面写的地址。
在1.html中点击“跳转”按钮,蹦到2.html:发起者是1.js。和上面超链接不一样的
由此可见GET的发生情况:
GET:
浏览器地址栏直接输入一个URL是GET请求;页面内容解析时引入(src方式)外部资源(js,图等)也是GET请求;
超链接请求(a 标签)也是GET请求,location.href指定跳转也是GET请求,
window.open(url)也是GET请求;
form表单当method为GET时是GET请求;
HTTP协议中,Request Header中的第一行,也就是起始行(请求行)的长度不能超过1024字节:
GET /test/index.html?user=guoyu&age=28 HTTP/1.1
既然起始行长度小于1KB,那么里面追加到URL后面的参数肯定不能大于1KB。因为还有其他内容,空格等,并且中文编码后占据更大的体积,中文必须编码,因为请求行中只能有ASCII字符。
说了那么多,什么时候才有POST请求呢?
其实只有一种:POST方式提交表单。这也就解释了为什么在ajax的时候,如果遇到POST请求,需要设置头信息:
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');//修改请求头,模仿form提交
另外,form表单中,GET提交,password也会明文显示,很不安全。换成POST就安全了吗?依然不安全,代码如下:
1.html提交跳转到2.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form method="POST" action="1.html">
<input type="text" value="郭宇"><br>
<input type="password" name="mypwd"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
以上,地址栏看不见了,Form Data中依旧可以看见密码,安全等级其实和GET没啥区别。
GET请求,如果form表单的action中添加参数能传到服务器吗?不能!action中带的参数会自动被删掉,num=3最后是找不到的。
<form method="GET" action="1.html?num=123">
<input type="text" name="userName" value="郭宇"><br>
<input type="password" name="mypwd"><br>
<input type="submit" value="提交">
</form>
那么POST呢,POST请求,如果form表单的action中添加参数能传到服务器吗?能!所以,POST不仅能把表单里的内容传给服务器,居然还能把action里追加的参数传过去,貌似干了GET干的事,因为后面追加的是查询字符串。
<form method="POST" action="1.html?num=123">
<input type="text" name="userName" value="郭宇"><br>
<input type="password" name="mypwd"><br>
<input type="submit" value="提交">
</form>
手动GET请求
前面介绍了好多种请求方式,其中GET和POST都是浏览器发出的请求,其他的请求,浏览器是不能发出的,只能靠手动请求,比如代码,或者命令行。
手动发起,用telnet 命令,也就是说访问一个网站的页面不一定非得用浏览器,在终端命令行就可以把html文档请求过来,只不过,浏览器多了个渲染引擎能把页面变成美丽的界面罢了。
1.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Telnet test guoyu</title>
</head>
<body>
<div>this is a telnet test</div>
<div>2017/06/01</div>
</body>
</html>
打开终端:输入:telnet 127.0.0.1 80,然后回车。
严格按照http协议的格式在终端上输入,(Host不能少),输入后回车:
GET /test/1.html HTTP/1.1
Host 127.0.0.1
接着上面,回车后,就相当于给服务器发送了一个GET请求,要访问1.html文件,紧接着服务器会按照你发的两行请求给你返回1.html,见下图:
这就是很单纯的HTTP的GET请求,非常清晰明了,只不过返回的html文件没有浏览器的渲染引擎进行渲染罢了!
当然,并没有完,当1.html完整的返回之后,大约等了几秒钟,终端尾部突然出现一行字:Connection closed by foreign host. 啥意思呢?过了几秒钟才断开连接啊,为啥?因为HTTP/1.1的Conection: keep-alive,前面已经讲过了的!
手动POST请求
POST请求是可以有请求主体的,下文会讲到的。而且请求主体和请求头要有回车换行,如下:
POST /test/2.html HTTP/1.1
Host: 127.0.0.1
uname=guoyu&upwd=123456
和上面的GET一样,终端输入:telnet 127.0.0.1 80,然后回车。
按照http协议格式输入上面的POST请求,然后服务器返回,过几秒钟后,断开连接。
手动HEAD请求
HEAD请求是浏览器发不出去的,终端上试试。
HEAD /test/1.html HTTP/1.1
Host: 127.0.0.1
步骤同上,最后也是几秒后断开!HEAD请求发出后,服务器只返回响应报头,并没有发送响应主体,只是检查资源是否改过(时间,大小等是否变化,用于缓存)
DELETE /test/1.html HTTP/1.1
Host: 127.0.0.1
返回:
HTTP/1.1 405 Method Not Allowed
其他命令,PUT等,自行测试!
<form method="POST" action="1.html?num=123" enctype="multipart/form-data">
<input type="text" name="userName" value="郭宇"><br>
<input type="password" name="mypwd"><br>
<input type="file" name="photo"><br>
<input type="submit" value="提交">
</form>
如果要上传文件到服务器,一定要用enctype=”multipart/form-data”
补充:客户端可以给服务器提交内容的类型只有三类如下:(但服务器给客户端那种类就多了)
- text/plain
- application/x-www-form-urlencoded
- multipart/form-data
GET /img/1.jpg HTTP/1.1
HEAD /img/1.jpg HTTP/1.1 (缓存,只要某个资源的头部信息,不要资源本身,如果看到这个资源的修改时间一直没变,说明资源没变过,不用再传送一遍)
请求行和请求方法 请求消息的起始行称为“请求行”,包含如下三部分:
<method> <request-RUL> <version>
请求体并不是每种方法都有,实际上,只有POST和PUT方法才有请求体,看下图:
请求头
1,通用头部:请求消息和响应消息中都可以使用
Connection
Date
Cache-Control
Pragma
2,请求专用头部:只能出现在请求消息中
Host
Referer
User-Agent
Client-IP
Accept
If-Modified-Since
Cookie
3,实体头部:描述消息主体特征
Location
Content-Length
Content-Type
Expires
Last-Modified
请求消息的三部分
1,起始行/请求行
请求方法 空格 请求URI 空格 所用协议
2,消息头部/请求头部
3,消息主体/请求正文
响应消息的三部分
1,起始行/响应行
2,消息头部/响应头部
3,消息的主体/响应正文