node:http
在 http 的服务框架等选择上,有 express、egg、koa 等诸多框架选择,其底层都是基于 NodeJs 的原生模块 node:http
虽然原生的方式使用不便,我们一般不会直接使用,但是有必要了解其基本原理和使用方式
创建一个服务器
创建一个简易的 http 服务器,返回 hello world
import http from 'node:http'
const server = http.createServer((req, res) => {
res.end("hello world")
})
server.listen(3000)启动后,浏览器访问 http://localhost:3000 即可看到内容
路由
通过 req.url 判断客户端请求的路径,服务端做出对应的响应
const server = http.createServer((req, res) => {
if(req.url === '/home') {
res.end("home page")
} else if(req.url === '/about') {
res.end("about page")
}
})方法
通过 req.method 判断客户端的请求方法
const server = http.createServer((req, res) => {
if(req.url === '/home' && req.method === 'GET') {
res.end("home page")
}
})MVC 封装
controller 封装
我们可以对 http 进行一个简易的封装,通过不同的路由,映射到不同的 controller 处理,使逻辑更加清晰
Controller 是控制器,负责处理请求,接收请求参数,调用服务获取业务数据,返回响应结果
// 控制器
const controller = {
index(req, res) {
res.end('This is index page')
},
home(req, res) {
res.end('This is home page')
},
_404(req, res) {
res.end('404 Not Found')
}
}
// 路由器
const router = (req, res) => {
if( req.url === '/' ) {
controller.index(req, res)
} else if( req.url.startsWith('/home') ) {
controller.home(req, res)
} else {
controller._404(req, res)
}
}service 封装
我们在加入 service 层
Service 服务层,负责业务逻辑处理,不直接依赖请求、响应对象
// controllers/home.js
class HomeController {
constructor(service) {
this.service = service;
}
handleRequest(req, res) {
// 处理 / 首页请求
}
}
// controllers/user.js
class UserController {
constructor(service) {
this.service = service;
}
handleRequest(req, res) {
// 处理 /user 请求
}
}
// server.js
import http from 'http'
import HomeController from './controllers/home'
import UserController from './controllers/user'
const homeController = new HomeController()
const userController = new UserController()
http.createServer((req, res) => {
if (req.url === '/') {
homeController.handleRequest(req, res);
} else if (req.url === '/user') {
userController.handleRequest(req, res);
}
})看起来就很像 egg 等 MVC 框架了
实现简易洋葱模型
type MiddlewareType = (ctx: any, next: () => void) => Promise<any>
const compose = function(middlewares: Array<MiddlewareType>) {
return function(ctx: any, next?: () => {}) {
function dispatch(i: number) {
if (i === middlewares.length) {
return Promise.resolve();
}
let fn = middlewares[i]
return fn(ctx, () => {
dispatch(i + 1)
})
}
return dispatch(0)
}
}
let middleware: Array<MiddlewareType> = [];
let context = {
data: []
};
middleware.push(async(ctx, next) => {
console.log('action 001');
ctx.data.push(2);
await next();
console.log('action 006');
ctx.data.push(5);
});
middleware.push(async(ctx, next) => {
console.log('action 002');
ctx.data.push(2);
await next();
console.log('action 005');
ctx.data.push(5);
});
middleware.push(async(ctx, next) => {
console.log('action 003');
ctx.data.push(2);
await next();
console.log('action 004');
ctx.data.push(5);
});
const fn = compose(middleware);
fn(context)
.then(() => {
console.log('end');
console.log('context = ', context);
});
// 结果显示
// "action 001"
// "action 002"
// "action 003"
// "action 004"
// "action 005"
// "action 006"
// "end"
// "context = { data: [1, 2, 3, 4, 5, 6]}"