从零开始使用 NodeJS 创建基本的 HTTP 服务器
在本博客中,我们将从头开始创建一个 HTTP 服务器来处理 GET、POST、PUT、DELETE 请求方法类型。
为了使代码正常运行,我们需要在机器上安装 Node.js。我们将使用 Node.js自带的“ http ”模块来获取请求和响应对象,不会使用任何其他自定义库。
创建 HTTP 服务器的步骤
- 使用http模块创建服务器并添加监听器。
- 对请求进行必要的录入检查
- 提取请求方法类型
- 编写 HTTP GET 请求的处理程序
- 编写 HTTP POST 请求的处理程序
- 编写 HTTP PUT 请求的处理程序
- 编写 HTTP DELETE 请求的处理程序
1. 使用http模块创建服务器并添加监听器
首先,我们需要创建一个服务器来监听某个特定的端口。这样,如果有任何请求到达该端口,监听器就会被调用。
我们可以使用http模块来实现这一点。
const server = http.createServer(requestListener);
server.listen(8090);
createServer方法接受监听器作为参数。listen方法接受一个端口号作为参数,该端口号将持续监听该端口。
我们来看看空的requestListener方法是什么样子的。
const requestListener = function (req, res) {
//all the code goes inside it
}
2. 对请求进行必要的录入检查
假设我们希望服务器支持 REST API,并且我们希望对请求对象进行以下检查:
- Content-Type 为 application/json
- Accept 是 application/json。
我们将使用 req 对象来获取标头详细信息并检查所需的值。
const REQUIRED_CONTENT_TYPE = 'application/json';
const ACCEPT_ENCODING_1 = 'application/json';
const ACCEPT_ENCODING_2 = '*/*';
const entryCheck = function (req) {
const contentType = req.headers["content-type"];
if (!contentType.includes(REQUIRED_CONTENT_TYPE)) {
throw new Error("Sorry we only support content type as json format.");
}
const accept = req.headers["accept"];
if (!(accept.includes(ACCEPT_ENCODING_1) ||
accept.includes(ACCEPT_ENCODING_2))) {
throw new Error("Sorry we only support accept json format.");
}
}
让我们来了解一下发生了什么。
- 首先,我们声明服务器将支持的 content-type 和 accept 标头的常量。
- Next entryCheck方法用于检查请求头是否包含所需且匹配的详细信息。
- 如果 Content-type 或 Accept 不匹配,我们将抛出错误。
现在让我们看看如何从类型监听器中调用此方法。
const requestListener = function (req, res) {
try {
entryCheck(req);
} catch (error) {
res.writeHead(400);
res.end(error.message);
}
- writeHead方法接受一个 HTTP 状态码作为参数,可以是任何有效的状态码。它还有一些可选参数,第二个参数是状态消息,第三个参数是请求头。
- end方法接收将要显示给用户的响应体。执行此方法后,响应将被发送回服务器,整个请求-响应过程即告完成。
注意:我们可以根据条件添加多个条目检查,例如 cookie、主机名/IP 地址、特定标头等。
3.提取请求方法类型
我们需要知道HTTP方法类型,以便分别处理每种方法。
const requestListener = function (req, res) {
try {
entryCheck(req);
const methodType = req.method.toUpperCase();
......
请求对象的 method 属性提供了 Http 方法类型,例如 GET、POST、PUT、DELETE。
接下来,我们可以使用 switch 语句来区别处理不同类型的 HTTP 请求。
....
switch(methodType){
case 'GET':
break;
case 'POST':
break;
case 'PUT':
break;
case 'DELETE':
break;
}
4. 编写 HTTP GET 请求的处理程序
HTTP GET 请求通常用于通过发送唯一详细信息来查找现有对象。
我们可以在每种 Http 方法类型中简单地返回一个通用响应。
case 'GET':
res.writeHead(200);
res.end(`We received ${methodType} type request`);
break;
但是,我们不只是返回简单的响应,而是创建一个对象并对其应用操作。
我们考虑一个包含以下字段的员工对象:
{
"_id": "5ec02a534587193b1c607e2c",
"name": {
"first": "Pace",
"last": "Simmons"
},
"company": "MOLTONIC",
"email": "pace.simmons@moltonic.co.uk",
"phone": "+1 (941) 562-2930",
"address": "274 Dikeman Street, Somerset, Nevada, 6375"
}
我们将有一个包含上述员工对象数组的对象。
let employeeData = [
{
"_id": "5ec02a534587193b1c607e2c",
"name": {
"first": "Pace",
"last": "Simmons"
},
"company": "MOLTONIC",
"email": "pace.simmons@moltonic.co.uk",
"phone": "+1 (941) 562-2930",
"address": "274 Dikeman Street, Somerset, Nevada, 6375"
},
......
]
考虑一个 GET 请求,我们将通过提供_id值来请求特定员工的详细信息。
localhost:8090/5ec02a53d8ba79b6992ba757
现在我们需要一个方法,用于在对象数组中查找请求的_id 。我们将编写一个方法,根据_id搜索员工:
let findEmployee = (id) => {
return employeeData.find((employee) => {
if (employee._id === id)
return employee;
});
}
让我们重写 GET HTTP 处理程序代码
const requestListener = function (req, res) {
....
case 'GET':
getMethodHandler(url, req, res);
break;
....
}
const getMethodHandler = (url, req, res) => {
const employeeId = url.substring(1);
const employee = findEmployee(employeeId);
if (!employee) {
res.writeHead(400);
res.end(`The employee with id ${employeeId} is not present.`);
return;
}
res.writeHead(200);
res.end(JSON.stringify(employee));
}
我们编写了一个单独的方法
- 首先,我们找到了请求的_id
- 我们将该_id传递给findEmployee方法以获取员工对象。
- 接下来,我们检查是否找到员工对象,如果找不到,则抛出错误。
- 如果一切顺利,我们会将员工对象返回到响应正文中。
5. 编写 HTTP POST 请求的处理程序
HTTP POST 请求通常用于插入新对象。在本例中,我们将把接收到的员工对象添加到数组中。让我们编写该方法的代码。
let addEmployee = (employee) => {
employeeData.push(employee);
}
接下来,我们需要处理 POST 请求并解析请求体,以获取我们需要插入的员工对象:
const requestListener = function (req, res) {
....
case 'POST':
getRequestBodyAndGenerateResponse(req, res, postMethodHandler);
break;
....
}
const getRequestBodyAndGenerateResponse = (req, res, callback) => {
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
callback(res, JSON.parse(body));
});
}
const postMethodHandler = (res, body) => {
try {
let reqBody = body;
addEmployee(reqBody)
res.writeHead(200);
res.end(`The Employee object with id is ${reqBody._id} added.`);
}
让我们来理解一下我们在这里做了什么。
- 我们定义了一个方法getRequestBodyAndGenerateResponse (req, res, postMethodHandler)。
- 此方法通过监听“ data ”事件从 req 对象读取数据,并将其附加到一个变量体中。
- 一旦触发“ end ”事件,表示请求体已完全读取,它将字符串解析为 JSON 并调用传递给它的回调函数。
- 这个回调函数用于准备响应对象。
- 在回调函数中,我们首先将员工添加到员工数组中。
- 然后准备回复并将其发送给用户。
6. 编写 HTTP PUT 请求的处理程序
HTTP PUT 请求通常用于更新旧对象。在本例中,如果数组中存在接收到的员工对象,我们将更新该对象。让我们编写该方法的代码。
let findAndReplace = (employee) => {
let employeeFound = findEmployee(employee._id);
if (employeeFound) {
for (var key in employee) {
employeeFound[key] = employee[key];
}
return true;
} else {
return false;
}
}
接下来,我们需要处理 PUT 请求并解析请求体,以获取我们需要更新的员工对象:
const requestListener = function (req, res) {
....
case 'PUT':
getRequestBodyAndGenerateResponse(req, res, putMethodHandler);
break;
....
}
const putMethodHandler = (res, body) => {
let reqBody = body;
findAndReplace(reqBody);
res.writeHead(200);
res.end(`The Employee object with id is ${reqBody._id} replaced.`);
}
让我们来理解一下我们在这里做了什么。
- 我们定义了一个方法getRequestBodyAndGenerateResponse (req, res, putMethodHandler)。
- 此方法通过监听“ data ”事件从 req 对象读取数据,并将其附加到一个变量体中。
- 一旦触发“ end ”事件,表示请求体已完全读取,它将字符串解析为 JSON 并调用传递给它的回调函数。
- 这个回调函数用于准备响应对象。
- 在回调函数中,我们首先更新员工数组中接收到的员工对象。
- 然后准备回复并将其发送给用户。
7. 编写 HTTP DELETE 请求的处理程序
HTTP DELETE 请求通常用于删除现有对象。在本例中,我们将从数组中删除接收到的员工对象_id 。让我们编写该方法的代码。
let deleteEmployee = (id) => {
let length = employeeData.length;
while (length--) {
if (employeeData[length]
&& employeeData[length]["_id"] === id) {
employeeData.splice(length, 1);
return true;
}
}
return false;
}
接下来,我们需要处理 DELETE 请求,获取员工的_id ,并从数组中删除该对象。
const requestListener = function (req, res) {
....
case 'PUT':
deleteMethodHandler(url, req, res);
break;
....
}
const deleteMethodHandler = (url, req, res) => {
const employeeId = url.substring(1);
const response = deleteEmployee(employeeId);
res.writeHead(200);
res.end(`The employee with id ${employeeId} is deleted.`);
}
让我们来理解一下我们在这里做了什么。
- 首先,我们找到了请求的_id
- 我们将该_id传递给deleteEmployee方法以删除员工对象。
- 如果一切顺利,我们将删除员工对象。
- 然后准备回复并将其发送给用户。
你可以在这里找到上面的代码。我尝试通过分离数据、方法并使用JS的模块导出/导入功能,将其转换为模块化格式。
如果你喜欢这篇文章,别忘了点赞哦!👏
谢谢!