背景
今天没事看了之前写过的博客,发现搭建HTTP服务器的博客并不细致完整;也无任何路由处理,以及响应处理。秉着对读者和自己负责的态度,决定再写一个更新篇。本文章将以以下目录顺序去介绍:
为了便于新手理解,在开始之前,先来了解一下几个概念(老手可直接跳过)。
概述
1. 网站的组成
网站应用程序主要分为两大部分:客户端和服务器端。
- 客户端
在浏览器中运行的部分,就是用户看到并与之交互的界面程序。使用HTML、CSS、JavaScript构建。 - 服务器端:
在服务器中运行的部分,负责存储数据和处理应用逻辑。
两者的关系如图所示:
2. 超文本传输协议
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)规定了如何从网站服务器传输超文本到本地浏览器,它基于客户端服务器架构工作,是客户端(用户)和服务器端(网站)请求和应答的标准。
报文
在HTTP请求和响应的过程中传递的数据块就叫报文,包括要传送的数据和一些附加信息,并且要遵守规定好的格式。报文的形式如图所示:
请求方式
- GET 请求数据
- POST 发送数据
PS:据我了解还有其他请求方式,感兴趣可自行百度了解。
响应状态码
- 200 请求成功
- 404 请求的资源没有被找到
- 500 服务器端错误
- 400 客户端请求有语法错误
PS:据我了解还有其他状态码,感兴趣可自行百度了解。
响应内容类型
- text/html 网页
- text/css css文件
- application/javascript JS脚本
- image/jpeg 图片
- application/json json数据
搭建服务端
这里就不再赘述如何搭建了,直接上代码,如果不清楚可以看我之前写的如何用Node搭建一个完整http服务器。
//引入http模块
const http = require('http')
//创建HTTP服务器
let app = http.createServer();
//绑定监听事件
//回调函数中req代表请求,res代表响应
app.on("request", (req, res) => {
});
//设置服务器端口
app.listen(2020,function(){
console.log('服务器已经开启,端口是2020')
})
此时建立开启的服务是没有任何任何响应的,我们可以在回调函数中直接加入响应内容,但是实际工作中是要根据用户的请求来响应内容的。那么如何来将用户的请求分类呢,这里我们就需要路由处理了。
路由处理
这里我们设计的思路是:
- 将所有的按请求方式分类
- 不同类别单独处理
- 获取请求的关键信息作为路由依据
首先将路由分类:
PS:这里只修改app事件中的代码,其余代码无改动,如改动会提示说明。必要文字叙述以注释的形式展开了,
//回调函数中req代表请求,res代表响应
app.on("request", (req, res) => {
//首先使用method获取求情方式
let method = req.method;
console.log(method)
});
运行服务端,在浏览器输入以下地址:
http://localhost:2020/
可以看到输出了请求方式:
使用分支语句,根据请求方法将请求分类,代码如下:
app.on("request", (req, res) => {
//首先使用method获取求情方式
let method = req.method;
// 分类
if(method=="GET")
{
}
else if(method=="POST"){
}
});
接着需要获取请求的关键信息,在这里我们使用一个Node中内置模块url,它当中提供了一个方法可以格式化请求连接。下面是它的使用:
PS:在代码的开始引入url模块。
const url = require('url')
app.on("request", (req, res) => {
//首先使用method获取求情方式
let method = req.method;
//格式化请求链接
let newUrl = url.parse(req.url);
// 使用对象.的方法获取关键信息pathname
let pathname = url.parse(req.url).pathname
// 分类
if(method=="GET")
{
console.log(newUrl)
console.log(pathname)
}
else if(method=="POST"){
}
});
此时浏览器中的请求为:
http://localhost:2020/index?name=alai&age=22
控制台的输出为:
接着我们假设网页中有三个页面:
- index.html
- list.html
- footer.html
下面我们编写路由结构,代码如下:
app.on("request", (req, res) => {
//首先使用method获取求情方式
let method = req.method;
//格式化请求链接
// let newUrl = url.parse(req.url);
// 使用对象.的方法获取关键信息pathname
let pathname = url.parse(req.url).pathname
// 分类
if (method == "GET") {
// console.log(newUrl)
// console.log(pathname)
// ||后面的意思是:默认将localhost:2020设为首页
if (pathname == "/index" || pathname == "/") {
console.log("响应首页")
}
else if (pathname == "/list") {
console.log("响应列表页")
} else if (pathname == "/footer") {
console.log("响应页脚")
}
}
else if (method == "POST") {
}
});
此时浏览器的访问链接如下:
http://localhost:2020/list
此时控制台的输出为:
此时虽然能够对请求进行分类,客户端访问链接以后仍然收不到任何内容。想要客户端收到消息就必须在服务端做出响应。
响应请求
在搭建服务端时,我们知道回调函数中参数res用来做出响应。这里我们介绍两个方法:
- res.writeHead()
- res.end()
res.writeHead()
它用来设置响应头,用法如下所示:
res.writeHead(200, {
"content-Type": "text/html;charset=utf8"
})
其中200表示我们在第一部分提到过的响应状态码,conten-type表示响应内容的类型,charset表示编码类型,一般为utf8。
res.end()
它用来响应内容,用法如下:
res.end("响应结果")
下面我们来编写简单响应内容,代码如下:
app.on("request", (req, res) => {
//首先使用method获取求情方式
let method = req.method;
//格式化请求链接
// let newUrl = url.parse(req.url);
// 使用对象.的方法获取关键信息pathname
let pathname = url.parse(req.url).pathname
// 分类
if (method == "GET") {
// console.log(newUrl)
// console.log(pathname)
res.writeHead(200, {
"content-Type": "text/html;charset=utf8"
})
// ||后面的意思是:默认将localhost:2020设为首页
if (pathname == "/index" || pathname == "/") {
res.end("<h1>欢迎来到首页</h1>")
}
else if (pathname == "/list") {
res.end("<h1>欢迎来到列表页</h1>")
} else if (pathname == "/footer") {
res.end("<h1>欢迎来到页脚</h1>")
} else {
res.writeHead(404, {
"content-Type": "text/html;charset=utf8"
})
res.end("<h1>你要找的页面走丢了</h1>")
}
}
else if (method == "POST") {
}
});
此时的运行结果如下所示:
列表页:
首页:
不存在的页面:
不存在时的状态码:
到这里,一个简易的http服务器就搭建好了,但是要用的实际项目中还有很长学习之路要走,还请继续坚持学习。
本博客旨在分享自己的所学和稀少的经验,也希望可以帮助初学者能更好的理解知识。
整理编写不易,看到这里如果对您有一丝丝帮助,还请我一个小小的赞,也可关注我,对我也是一种激励,工作之余尽量多分享内容。
鉴于笔者资历尚浅,如有疏漏不足之处欢迎指出,更欢迎兴趣相投者交流学习。下面是笔者的微信:
记2020年8月27日
于天津