Skip to content
0

node:http

在 http 的服务框架等选择上,有 expresseggkoa 等诸多框架选择,其底层都是基于 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]}"

Released under the MIT License.