NodeJs 简单的基于 http、url、querystring 模块的服务器项目示例(纯 NodeJs 库,无依赖第三方扩展包,支持跨域)
不依赖第三方扩展包,支持路由,支持简单的 GET、POST、PUT、DELETE 请求类型。
只有一个文件:server.js,源码如下:
// 文件路径:server.js
// 使用方法:node server.js,浏览器打开:http://127.0.0.1:3000
/**
* NodeJs 简单的基于 http、url、querystring 模块的服务器项目示例(纯 NodeJs 库,无依赖第三方扩展包)
*/
const NmHttp = require('http');
const NmUrl = require('url');
const NmQueryString = require('querystring');
// 引入 MD5 加密模块
const NmCrypto = require('crypto');
// 配置信息
const host = {
ip: '127.0.0.1',
port: 3000
}
// 简易的路由类
class Route {
routes = {};
getKey(method, url) {
return NmCrypto.createHash('md5').update(method + url).digest('hex').substring(8, 16);
}
hasRoute(method = '', url = '') {
return typeof (this.routes[this.getKey(method.toUpperCase(), url)]) != 'undefined';
}
getRoute(method = '', url = '') {
return this.routes[this.getKey(method.toUpperCase(), url)];
}
getRouteCallBack(method = '', url = '') {
return this.routes[this.getKey(method.toUpperCase(), url)].cb;
}
addRoute(method = '', url = '', cb = null) {
method = method.toUpperCase();
this.routes[this.getKey(method, url)] = {
method: method,
url: url,
cb: cb
};
}
get(url, cb) {
this.addRoute('GET', url, cb);
}
post(url, cb) {
this.addRoute('POST', url, cb);
}
put(url, cb) {
this.addRoute('PUT', url, cb);
}
delete(url, cb) {
this.addRoute('DELETE', url, cb);
}
header(url, cb) {
this.addRoute('HEADER', url, cb);
}
}
let routers = getRouters();
// ===== 核心功能 =====
// 创建服务实例
const server = NmHttp.createServer((req, res) => {
// 处理输入请求参数
// 解析地址。req.url=/,是相对路径,所以要加 baseUrl 参数
let urlObj = new NmUrl.URL(req.url, 'http://' + host.ip);
let urlPathName = urlObj.pathname; // 路由路径(?前面的相对路径)
// 获取 URL 参数对象
let urlParams = urlObj.searchParams;
// 获取表单提交的内容
let reqBody = '';
req.on('data', function (data) {
reqBody += data;
})
// 用于数据接收完成后再获取
req.on('end', function () {
let formParams = NmQueryString.parse(reqBody);
// 获取请求类型(GET,POST,DELETE...)
let reqMethod = req.method;
// 伪装请求类型处理,可以在表单中带 _method 文本域,值可为 GET,POST,PUT,DELETE。
if (urlParams.has('_method')) {
reqMethod = urlParams.get('_method');
}
if (typeof (formParams['_method']) != 'undefined') {
reqMethod = formParams['_method'];
}
// 返回内容
let content = {
status: 0,
msg: '路由错误 ',
data: {
method: reqMethod,
path: urlPathName
},
code: 404
};
// 简易路由处理
if (routers.hasRoute(reqMethod, urlPathName)) {
content = routers.getRouteCallBack(reqMethod, urlPathName)(urlParams, formParams);
}
// 处理跨域
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
res.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.setHeader("X-Powered-By",' 3.2.1')
// 处理输出
if (content.status == 0 && content.code == 404) {
res.writeHead(404);
res.end(JSON.stringify(content));
return;
}
res.statusCode = 200;
switch (typeof (content)) {
case 'object':
content = JSON.stringify(content);
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Length', Buffer.byteLength(content))
res.end(content)
break;
default:
if (content.indexOf('<') > -1 && content.indexOf('>') > -1) {
res.setHeader('Content-Type', 'text/html');
} else {
res.setHeader('Content-Type', 'text/plain');
}
res.setHeader('Content-Length', Buffer.byteLength(content))
res.end(content);
break;
}
})
})
// 开始监听
server.listen(host.port, host.ip, () => {
console.log(`服务器运行在 http://${host.ip}:${host.port}`);
})
// ===== 业务功能 =====
// 获取路由配置
function getRouters() {
let route = new Route();
// 测试 http://127.0.0.1:3000/test
route.get('/test',function(urlParams){
return {status:1,msg:'测试成功',data:{ts:new Date().getTime()},code:1}
})
// 默认首页
route.get('/', function (urlParams) {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>演示</title>
</head>
<body>
<h2>演示</h2>
<form target="_blank">
编号ID:<input type="number" name="id" value="1" /><br />
用户名:<input type="text" name="user_name" value="user-001" /><br />
密码:<input type="password" name="password" value="123456" /><br />
性别:<input type="radio" name="sex" value="1" checked /> 男 <input type="radio" name="sex" value="2" /> 女<br />
<hr>
路由地址:
<select id="routePicker">
<option value="">==请选择==</option>
<option value="/user/read" data-method="GET">获取用户信息</option>
<option value="/user/save" data-method="POST">新增用户</option>
<option value="/user/update" data-method="PUT">更新用户</option>
<option value="/user/delete" data-method="DELETE">删除用户</option>
</select><br />
<div id="routeInfo">路由信息:</div>
<hr>
<input type="hidden" name="_method">
<button type="submit">提交表单</button> <button type="reset">重置表单</button>
</form>
<script>
let elRouteInfo = document.querySelector('#routeInfo');
let elRoutePicker = document.querySelector('#routePicker')
let elForm = document.querySelector('form');
let elMethod = document.querySelector('input[name="_method"]');
elRoutePicker.addEventListener('change', function (evt) {
let elOption = this.options[this.selectedIndex];
elRouteInfo.innerHTML = '方式:' + elOption.dataset.method + ',路径:' + elOption.value;
elForm.setAttribute('method', elOption.dataset.method == 'GET' ? 'GET' : 'POST');
elForm.setAttribute('action', elOption.value);
elMethod.value = elOption.dataset.method;
})
elForm.addEventListener('submit', function (evt) {
evt.preventDefault();
if (!elForm.hasAttribute('method') || !elForm.hasAttribute('action')) {
alert('未选择路由地址');
return false;
}
elForm.submit();
})
</script>
</body>
</html>`;
})
route.get('/user/read', function (urlParams) {
let id = urlParams.has('id') ? parseInt(urlParams.get('id')) : 0;
return {
id: id,
user_name: 'user-' + id,
sex: 0
};
})
route.post('/user/save', function (urlParams, formParams) {
return {
status: 1,
msg: '保存成功',
data: formParams,
code: 0
}
})
route.put('/user/update', function (urlParams, formParams) {
let id = urlParams.has('id') ? parseInt(urlParams.get('id')) : 0;
return {
status: 1,
msg: '更新成功',
data: formParams,
code: 0
}
})
route.delete('/user/delete', function (urlParams, formParams) {
let id = urlParams.has('id') ? urlParams.get('id') : null;
if (id < 1 && typeof (formParams.id) != 'undefined') {
id = formParams.id;
}
if (!id) {
return {
status: 0,
msg: '编号无效',
data: null,
code: 0
}
}
return {
status: 1,
msg: '删除成功',
data: {
id: id
},
code: 0
}
})
return route;
}
运行:
node server.js
在浏览器打开如下URL
http://127.0.0.1:3000
界面效果: