HTTP协议
一、HTTP协议的概念
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
二、报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。
三、请求报文
请求方式 (Request Method)
- GET 请求数据
- POST 发送数据
请求地址 (Request URL)
app.on('request', (req, res) => {
req.headers // 获取请求报文
req.url // 获取请求地址
req.method // 获取请求方法
});
下面是对 req.headers 、req.url、 req.method 的输出结果
const http = require('http');
const app =http.createServer();
app.on('request',(req,res)=>{
console.log(req.headers);
console.log(req.method);
console.log(req.url);
res.end('响应文字');
});
app.listen(3000);
四、响应报文
HTTP状态码:
- 200 请求成功
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
内容类型
告诉客户端需要解析的内容类型,现在对于一些高级浏览器,可以不用指定类型,它自动会去识别或者说去分析内容的类型,但是对于一些老的浏览器就不行,就会出问题。
- text/html
- text/css
- application/javascript
- image/jpeg
- application/json
- ·······
设置内容类型
res.writeHead
在res里面有一个writHead
方法,它有两个参数,第一个参数是http状态码,第二个参数是一个对象,这个对象里面写的就是响应报文的信息。
app.on('request', (req, res) => {
// 设置响应报文
res.writeHead(200, {
'Content-Type': 'text/html;charset=utf8‘
});
});
五、HTTP请求与响应处理
1、请求参数
客户端向服务器端发送请求时,有时需要携带一些客户信息,客户信息需要通过请求参数的形式传递到服务器端,比如登录操作。
2、GET请求参数
GET请求参数被放置在浏览器地址栏中,例如:
http://localhost:3000/?name=zhangsan&age=20
GET请求参数获取需要借助Node的系统模块url,使用url模块用来处理url地址。
在URL这个内置模块中有一个方法parse
,parse的单词意思就是解析的意思,parse
的第一个参数就是需要解析URL地址,第二个参数是询问是否将查询的参数解析为对象形式,填ture
或false
。下面对url.parse()
进行输出,访问地址:
http://localhost:3000/?name=zhangsan&age=20
const http = require('http');
const url = require('url');
const app =http.createServer();
app.on('request',(req,res)=>{
console.log(url.parse(req.url));//对`url.parse()`进行输出
res.writeHead(200, {
'Content-Type': 'text/html;charset=utf8'
});
res.end('响应文字');
});
app.listen(3000);
下面将url路径的各个部分解析出来并返回对象
const http = require('http');
// 导入url系统模块 用于处理url地址
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
// 将url路径的各个部分解析出来并返回对象
// true 代表将参数解析为对象格式
let {query} = url.parse(req.url, true);
console.log(query);
});
app.listen(3000);
app.js
// 用于创建网站服务器的模块
const http = require('http');
// 用于处理url地址
const url = require('url');
// app对象就是网站服务器对象
const app = http.createServer();
// 当客户端有请求来的时候
app.on('request', (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('网站服务器启动成功');
3、POST请求参数
POST请求参数被放置在请求体(请求报文)中进行传输,如图:
由于POST参数没有放在地址栏中因此不能使用url
来进行处理,实际上post参数是通过事件的方式接收的,获取POST参数需要使用data
事件和end
事件。POST参数理论上可传输的数据是无限大的。
data事件: 当请求参数传递的时候触发data事件
end事件: 当参数传递完成的时候触发end事件
使用 Node中的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(querystring.parse(postData));
});
});
post.js
// 用于创建网站服务器的模块
const http = require('http');
// app对象就是网站服务器对象
const app = http.createServer();
// 处理请求参数模块
const querystring = require('querystring');
// 当客户端有请求来的时候
app.on('request', (req, res) => {
// post参数是通过事件的方式接受的
// data 当请求参数传递的时候出发data事件
// end 当参数传递完成的时候出发end事件
let postParams = '';
req.on('data', params => {
postParams += params;
});
req.on('end', () => {
console.log(querystring.parse(postParams));
});
res.end('ok');
});
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
4、路由
路由是指客户端请求地址与服务器端程序代码的对应关系。简单的说,就是请求什么响应什么。
上面这张图,图的上方的是客户端的请求地址以及请求方式。下面是客户端请求地址所对应的服务器端处理代码。两者要进行匹配对应就需要路由。实际上呢在路由当中,就是一堆判断代码。判断客户端请求的地址是什么?然后调用服务器端对应的程序处理代码,这是对路有概念的理解。
接下来看一下实现路由的核心代码。当客户端发来请求的时候通过req.url
来获取到客户端的请求路径,但是呢,这个请求路径是包含GET
请求参数的,所以通过系统模块URL下面的parse
方法获取到不携带请求参数的请求路径,然后在对请求路径进行判断,如果判断条件成立了,就可以执行请求路径对应的逻辑处理代码,接下来就可以调用关于首页的代码。如果输入的是 /list 。就调用响应列表页的内容。如果用户访问的这个请求地址在服务器端没有对应的处理代码,就告诉客户端当前访问的页面是不存在的。这就是核心的路由实现代码。
// 当客户端发来请求的时候
app.on('request', (req, res) => {
// 获取客户端的请求路径
let { pathname } = url.parse(req.url);
if (pathname == '/' || pathname == '/index') {
res.end('欢迎来到首页');
} else if (pathname == '/list') {
res.end('欢迎来到列表页');
} else {
res.end('抱歉, 您访问的页面出游了');
}
});
// 1.引入系统模块http
// 2.创建网站服务器
// 3.为网站服务器对象添加请求事件
// 4.实现路由功能
// 1.获取客户端的请求方式
// 2.获取客户端的请求地址
const http = require('http');
const url = require('url');
const app = http.createServer();
app.on('request', (req, res) => {
// 获取请求方式
const method = req.method.toLowerCase();
// 获取请求地址
const pathname = url.parse(req.url).pathname;
res.writeHead(200, {
'content-type': 'text/html;charset=utf8'
});
if (method == 'get') {
if (pathname == '/' || pathname == '/index') {
res.end('欢迎来到首页')
}else if (pathname == '/list') {
res.end('欢迎来到列表页')
}else {
res.end('您访问的页面不存在')
}
}else if (method == 'post') {
}
});
app.listen(3000);
console.log('服务器启动成功')
5、静态资源
服务器端不需要处理,可以直接响应给客户端的资源就是静态资源,例如CSS、JavaScript、image文件。
如:http://www.xialinzhijia.club/img/55.jpg
无论谁访问这个地址都是这张图片。
实现静态资源的访问
- 获取用户的请求路径
- 将用户的请求路径转换为实际的服务器硬盘路径
- 读取文件响应给客户端
注意: 对于一些低级浏览器需要设置文件类型,但是怎么样去解决根据我当前请求的这个文件它的类型。去设置的相应的类型。这个时候我们要用到一个叫做mime
的第三方模块,这个第三方模块的功能就是可以根据你当前的请求路径分析出这个资源的类型,然后把这个文件类型通过返回值的方式返回出来。
首先把第三方模块给他引进来。这个模块下面,有一个方法叫做getType
就是用来干这个事的,使用mime
下面的方法。然后把路径传进去。那么它就可以根据路径给你返回这个资源的类型。
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const mime = require('mime');
const app = http.createServer();
app.on('request', (req, res) => {
// 获取用户的请求路径
let pathname = url.parse(req.url).pathname;
pathname = pathname == '/' ? '/default.html' : pathname;
// 将用户的请求路径转换为实际的服务器硬盘路径
//public为静态资源储存文件夹
let realPath = path.join(__dirname, 'public' + pathname);
let type = mime.getType(realPath);
// 读取文件
fs.readFile(realPath, (error, result) => {
// 如果文件读取失败
if (error != null) {
res.writeHead(404, {
'content-type': 'text/html;charset=utf8'
});
res.end('文件读取失败');
return;
};
res.writeHead(200, {
'content-type': type;charset=utf8
});
res.end(result);
});
});
app.listen(3000);
console.log('服务器启动成功')
6、动态资源
相同的请求地址不同的响应资源,这种资源就是动态资源。
建议仔细阅读文章:静态web和动态web的区别与联系
7、客户端请求途径
GET方式
- 浏览器地址栏
- link标签的href属性
- script标签的src属性
- img标签的src属性
- Form表单提交
POST方式
- Form表单提交