请求响应原理及HTTP协议,请求报文与响应报文
一、服务器端基础概念
1.1 网站的组成
网站应用程序主要分为两大部分:客户端和服务器端。
客户端:在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使用HTML、CSS、JavaScript构建。
服务器端:在服务器中运行的部分,负责存储数据和处理应用逻辑。
1.2 Node网站服务器
能够提供网站访问服务的机器就是网站服务器,它能够接收客户端的请求,能够对请求做出响应。
一台电脑需要安装Node代码运行环境,使用Nodejs创建一个能接收对象和响应请求的对象,就是Node网站服务器(可以没有显示器鼠标键盘,只有一个主机)
电脑 ➡️ Node ➡️ 响应请求的对象
1.3 IP地址
互联网中设备的唯一标识。
IP是Internet Protocol Address的简写,代表互联网协议地址
1.4 域名
由于IP地址难于记忆,所以我们一般不会使用IP地址直接访问网站。于是产生了域名的概念,所谓域名就是平时上网所使用的网址。
http://www.hellocode.com => http://136.168.215.101/
虽然在地址栏中输入的是网址, 但是最终还是会将域名转换为ip才能访问到指定的网站服务器。
1.5 端口
端口是计算机与外界通讯交流的出口,用来区分服务器电脑中提供的不同的服务。(比如web server和email server)
1.6 URL
统一资源定位符,又叫URL(Uniform Resource Locator),是专为标识Internet网上资源位置而设的一种编址方式,我们平时所说的网页地址指的即是URL。
URL的组成
传输协议://服务器IP或域名:端口/资源所在位置标识
https://localhost:8080/news/789.html
- http:超文本传输协议。是一个基于请求与响应、无状态的、应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的WWW文件都必须遵守这个标准。设计 HTTP 的初衷是为了提供一种发布和接收HTML页面的方法。
- https:是身披SSL外壳的 HTTP。一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。
PS:TLS是传输层加密协议,前身是SSL协议,由网景公司1995年发布,有时候两者不区分。
1.7 开发过程中客户端和服务器端说明
在开发阶段,客户端和服务器端使用同一台电脑,即开发人员电脑。
客户端:浏览器
服务器端:Node.js
本地域名:localhost
本地IP:127.0.0.1
二、创建Web服务器
// 用于创建网站服务器的模块
const http = require('http');
// 用于处理url地址
const url = require('url');
// app对象就是网站服务器对象
const app = http.createServer();
// 当客户端有请求来的时候
app.on('request', (req, res) => {//req是请求对象 res是响应对象
// 获取请求方式
// req.method
console.log(req.method);
// 获取请求地址
// req.url
console.log(req.url);
// 获取请求报文信息
// req.headers
console.log(req.headers['accept']);
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
console.log(req.url);
// 1) 要解析的url地址
// 2) 将查询参数解析成对象形式
let { query, pathname } = url.parse(req.url, true);
console.log(query.name)
console.log(query.age)
if (pathname == '/index' || pathname == '/') {
res.end('<h2>欢迎来到首页</h2>');
}else if (pathname == '/list') {
res.end('welcome to listpage');
}else {
res.end('not found');
}
if (req.method == 'POST') {
res.end('post')
} else if (req.method == 'GET') {
res.end('get')
}
// res.end('<h2>hello user</h2>');
});
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
三、HTTP协议
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
3.1 报文
在 HTTP 请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式(键值对形式)。
请求报文:
由请求行(request line)、请求头(header)、空行 和 请求体(body,可选的、包含数据) 组成。一次 HTTP 请求会按顺序依次发送以上四个部分 (get 请求没有请求体)。
我们用谷歌浏览器来对照着查看:
依次点击找到请求报文 点击 View source 请求报文的一般格式-
请求行:请求方式(
位置展示<Request Method>
) 资源路径(<Request URL>
) 协议/版本
如GET /users/9a996b97d637/publications?page=1&count=10 HTTP/1.1
- 常用请求方式 - Request Method:GET 请求(获取)数据|POST 发送(添加)数据(更安全)|PUT 替换(修改)数据|DELETE 删除数据
- 请求地址 - Request URL:端口号后面的地址。
-
请求头:附加的用来告知服务器的一些信息,以键值对的形式出现。
以下列举部分常见属性:
请求头参数 | 描述 |
---|---|
Host | 目标服务器的主机名 (ip 地址或域名) |
User-Agent | 发起请求的客户端应用程序相关信息,如操作系统、浏览器等信息 |
Referer | 当前文档的完整 URL |
Accept | (Accept) 指定客户端能接收哪些响应信息类型,如:image/jpg, text/html,专业术语称为MIME Type |
Accept-Charset | (Accept) 指定客户端支持哪些字符集,如 gb2312 |
Accept-Encoding | (Accept) 指定客户端支持接收的内容编码(数据压缩)方式,如 gzip, deflate |
Accept-Language | (Accept) 指定客户端接受那些语言,如 zh-CN |
Authorization | (安全相关) 客户端提供给服务端进行权限认证的信息 |
Cookie | (安全相关) 客户端携带的 cookie 信息(通常会存储一个sessionID,通过这个令牌让服务端鉴权) |
Content-Type | (响应头通用) 内容类型,请求的与实体对应的MIME信息。如果是 post 请求,会有这个参数,默认值为 application/x-www-form-urlencoded,表示请求体内容使用 url 编码 |
Content-Length | (响应头通用) 请求体数据长度 |
Connection | (响应头通用) 指定客户端和服务器之间请求/响应连接的类型,如:Keep-Alive 表示保持 TCP 持久连接 |
Transfer-Encodeing | (响应头通用) 告知接收端为了报文的可靠传输,对报文使用什么样的编码格式 |
Cache-Control | (响应头通用-缓存相关) 指定请求和响应遵循的缓存机制 |
If-Modified-Since | (缓存相关) 服务器会用该值与所请求资源的最后修改时间作对比,如相同返回304 和空响应体(节省带宽),告诉客户端资源未修改可直接读取客户端缓存;不同则返回200 和最新的资源。值是上一次请求该资源时响应头的 Last-Modified
|
If-None-Match | (缓存相关) 服务器会用该值与所请求资源的 ETag 作比较,匹配会返回304 告诉客户端直接使用本地缓存即可;不一致则返回200 和新资源(还有新 ETag)。同时存在时,优先级高于 If-Modified-Since
|
- 空白行:请求报文用来分隔 请求头 和 请求体
Node 访问请求方法(method)、 请求地址(url) 和 请求头(headers)
const http = require('http');
const app = http.createServer();
app.on('request',(req, res) => {
// 获取请求方式、请求地址 和请求头
const { method, url, headers } = req;
// 获取请求头某个属性信息
const userAgent = headers['user-agent'];
})
-
请求体(body):请求时携带的有效载荷数据。区别于 url 中的查询参数。可以简单地理解为,如果是 POST/PUT 请求,就有请求体。
响应报文:
由 状态行、响应头,空行,响应体(数据) 组成。服务器响应时,会按顺序依次发送以上四个部分。
-
状态行:协议/版本号 状态码 状态值(状态描述)
如:HTTP/1.1 200 OK
- 状态码 (Status Code):用以表示服务器 HTTP 响应状态的 3 位数字代码:
状态码 | 描述 |
---|---|
1xx | 提示信息,服务器收到请求,需要请求者继续执行操作 |
2xx | 成功,请求被成功接收并处理 200 |
3xx | 重定向相关 304--所请求的资源未修改,客户端会直接访问这个资源的缓存,不会返回任何资源 |
4xx | 客户端错误 404--请求的资源没有被找到 400-客户端请求存在语法错误 |
5xx | 服务端错误,500--服务器在处理请求的过程中发生了错误 502/504--充当网关或代理的服务器,从远端服务器接收到了一个无效的请求/未及时从远端服务器获取请求 |
- 响应头:类似请求头,告知客户端的附加信息,是一系列 key-value 值。
以下列举部分:
(部分通用属性已经在上面请求头部分列出,不再重复)
响应头参数 | 描述 |
---|---|
Server | 服务器软件名,如 Tengine |
Content-Encoding | 对主体执行的编码方式 |
Content-Type | 响应主体的内容类型和字符集 |
Location | 告知客户端实体实际上位于何处;用于重定向 |
Set-Cookie | (安全相关) 设置和页面关联的Cookie |
Proxy-Authenticate | (安全相关) 来自代理服务器的质询列表 |
Vary | (协商相关) 一个列表,内容来自于当前请求头的Key,比如Accept-Encoding、User-Agent等,用于发生相同请求的时候决定某个缓存的响应版本是否可以用来发给客户端 |
ETag | (缓存相关) 实体标记。服务端当前资源实体特定版本的唯一标识符,只要资源有变化就会重新生成。下一次请求该资源时会将这个值放入请求头的 If-None-Match 来和服务端的 ETag 作比较。 |
Expires | (缓存相关) 资源缓存过期时间。即客户端在这个时间前获取此资源都直接读取本地缓存,而不再向服务器请求,作用是控制请求的频率。Expires是http1.0的产物,已经被http1.1的 Cache-Control: max-age 替代,仍然存在是为了兼容性 |
Last-Modified | (缓存相关) 这个实体最后一次被修改的时间。与请求头的 If-Modified-Since 配合使用 |
Vary:
- Vary的内容来自于当前请求的请求头的Key,如
Accept-Encoding、User-Agent等
; - 缓存服务器进行某接口的请求结果数据缓存时,会将Vary一起缓存;
- 服务器缓存的Vary的内容会作为当前缓存数据是否可以作为请求结果返回给客户端的判断依据;
- 先通过响应数据中的Vary来判断当前缓存中同请求的数据的Vary是否失效,如果缓存中的Vary与服务器刚拿到的Vary不一致,则可以进行更新。
- 当Vary的值为“*”,意味着请求头中的所有信息都不可作为是否从缓存服务器拿数据的判断依据。
Content-Type:
响应的内容类型,告知客户端你发送的数据内容类型,浏览器应以什么形式、什么编码读取这个文件(否则可能解析错误而出现乱码)。
文件类型有:text/plain(纯文本)-对应.txt
、text/html-对应.html
、text/css-对应.css
、application/javascript-对应.js
、image/jpeg-对应.jpg或.jpeg
、image/png-对应.png
、application/json-对应.json
res.writeHead(200,{
'content-type':'application/json; charset=utf-8'
})
-
空白行:同上,用来分隔 响应头 和 数据。
-
响应体:响应的data
3.2 请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
1. GET 请求参数
参数被放置在浏览器地址栏中,例如:http://localhost:3000/abc/xyz?name=zhangsan&age=20
- 请求参数是
?
后面的部分,即name=zhangsan&age=20
(通常我们称之为 查询参数)
// 系统模块 url 用来处理 url 地址
const url = require('url')
app.on('request', (req, res) => {
// `url.parse(req.url)` 将 url 路径的各个部分解析出来并返回对象
// 第二个参数 true表示把查询参数 (url对象的query属性值) 解析成对象格式
let { query, pathname } = url.parse(req.url, true); // 解构赋值获取查询参数 query
console.log(query.name) // zhangsan
console.log(query.age) // 20
console.log(pathname) // /abc/xyz
}
2. POST 请求参数
- 参数被放置在请求体中进行传输(Form Data)
- 获取POST参数需要使用 data 事件和 end 事件
- 使用 querystring 系统模块将参数转换为对象格式
// 导入系统模块querystring 用于将HTTP参数转换为对象格式
const querystring = require('querystring');
app.on('request', (req, res) => {
let postData = '';
// 监听参数传输事件
req.on('data', (chunk) => postData += chunk;);
// 监听参数传输完毕事件
req.on('end', () => {
console.log(postData);// 字符串
console.log(querystring.parse(postData)); //把接收到参数字符串转化为对象
});
res.end('ok');
});
3.2 路由
http://localhost:3000/index
http://localhost:3000/login
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
// 当客户端发来请求的时候
app.on('request', (req, res) => {
// 获取请求方式
const method = req.method.toLowerCase();
// 获取客户端的请求路径
// const pathname = url.parse(req.url).pathname;
let { pathname } = url.parse(req.url);
if (pathname == '/' || pathname == '/index') {
res.end('欢迎来到首页');
} else if (pathname == '/list') {
res.end('欢迎来到列表页页');
} else {
res.end('抱歉, 您访问的页面出游了');
}
});
3.3 静态资源
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如 CSS、JavaScript、image文件。
举例:http://localhost:8080/images/logo.png
3.4 动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
http://localhost:8080/article?id=123
http://localhost:8080/article?id=456
3.5 客户端请求途径
- GET 方式
- 浏览器地址栏访问
- link 标签的 href 属性
- script 标签的 src 属性
- img 标签的 src 属性
- Form 表单提交 (表单的默认跳转行为)
- POST 方式
- Form 表单提交 (在form表单标签里写上
method='post'
)