# NodeJs 实战——原生 NodeJS 轻仿 Express 框架从需求到实现(一)

栏目: Node.js · 发布时间: 5年前

内容简介:这篇文章是一个系列的文章的第一篇,主要是自己实现一个express的简易版框架,加深对nodejs的理解。我们从一个经典的 Hello World 开始,这是 Express 官方文档的第一个实例, 代码如下运行 helloworld.js

这篇文章是一个系列的文章的第一篇,主要是自己实现一个express的简易版框架,加深对nodejs的理解。

确认需求

我们从一个经典的 Hello World 开始,这是 Express 官方文档的第一个实例, 代码如下

const express = require('express');
const app = express();

app.get('/', (req, res) => res.send('Hello World!'));

app.listen(3000, () => console.log('Example app listening on port 3000!'));
复制代码

运行 helloworld.js

node helloworld.js
复制代码

在浏览器上打开 http://localhost:3000 ,网页将显示 Hello World。

代码实现

由 Hello World 实例分析,我们可以看出 express 返回了一个函数,而执行这个函数会返回一个类的实例,实例具有 get 和 listen 两个方法。

第一步,创建目录

首先我们先构建下图的目录结构

# NodeJs 实战——原生 NodeJS 轻仿 Express 框架从需求到实现(一)

第二步,创建入口文件

其中,入口为 express.js 文件,入口非常简单

const Application = require('./application');
function express() {
  return new Application();
}
module.exports = express;
复制代码

第三步,实现应用程序类 Application

应用程序类为 application.js 文件,在这次实现中我们要达到如下要求:

  • 实现 http 服务器
  • 实现 get 路由请求
  • 实现 http 服务器非常简单,我们可以参考 nodejs 官网的实现。
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});
server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
复制代码

参考该案例,实现 express 的 listen 函数。

listen: function (port, cb) {
    var server = http.createServer(function(req, res) {
        console.log('http.createServer...');
    });
    return server.listen(port, cb);
}
复制代码

当前 listen 函数包含了两个参数,但是 http.listen 里包含了许多重载函数,为了和 http.listen 一致,可以将函数设置为 http.listen 的代理,这样可以保持 express 的 listen 函数和 http.listen 的参数保持一致。

listen: function (port, cb) {
    var server = http.createServer(function(req, res) {
        console.log('http.createServer...');
    });

    return server.listen.apply(server, arguments);
}
复制代码

nodejs 后台服务器代码根据 http 请求的不同,绑定不同的逻辑。在 http 请求到服务器后,服务器根据一定的规则匹配这些 http 请求,执行与之相对应的逻辑,这个过程就是 web 服务器基本的执行流程。

对于这些 http 请求的管理,我们称之为—— 路由管理 ,每个 http 请求就默认为一个 路由 。 我们创建一个 router 数组用来管理所有路由映射,参考 express 框架,抽象出每个路由的基本属性:

  • path 请求路径,例如:/goods。
  • method 请求方法,例如:GET、POST、PUT、DELETE。
  • handle 处理函数
var router = [
  {
    path: '*',
    method: '*',
    handle: function(req, res) {
      res.writeHead(200, {
        'Content-Type': 'text/plain'
      });
    }
  }
];
复制代码

修改 listen 方法,将 http 请求拦截逻辑修改为匹配 router 路由表,循环 router 数组里的对象,当请求方法和路径一致时,执行回调函数 handler 方法。

listen: function(port, cb) {
    const server = http.createServer(function(req, res) {
        // 循环请求过来放入router数组的对象,当请求方法和路劲与对象一致时,执行回调handler方法
        for (var i = 1, len = router.length; i < len; i++) {
            if (
            (req.url === router[i].path || router[i].path === '*') &&
            (req.method === router[i].method || router[i].method === '*')
            ) {
            return router[i].handle && router[i].handle(req, res);
            }
        }
        return router[0].handle && router[0].handle(req, res);
    });
    return server.listen.apply(server, arguments);
}
复制代码

实现 get 路由请求非常简单,该函数主要是添加 get 请求路由。

get: function(path, fn) {
    router.push({
        path: path,
        method: 'get',
        handle: fn
    })
}
复制代码

完整的代码如下:

const http = require('http');
const url = require('url');

// 应用程序类
function Application () {
    // 用来保存路由的数组
    this.router = [[
        path: '*',
        method: '*',
        handler: function (req, res) {
            res.writeHead(200, {
                'Content-Type': 'text/plain'
            });
            res.end('404');
        }
    ]];
}

// 在Application的原型上拓展get方法,以便Application的实例具有该方法。
Application.prototype.get = function (path, handler) {
    // 将请求路由压入栈内
    this.router.push({
        path,
        method: 'get',
        handler
    });
}

// 在Application的原型上拓展listen方法,以便Application的实例具有该方法。
Application.prototype.listen = function () {
    const server = http.createServer(function(req, res) {
        if(!res.send) {
            // 拓展res的方法,让其支持send方法
            res.send = function (body) {
                res.writeHead(200, {
                    'Content-Type': 'text/plain'
                });
                res.end(body);
            }
        }
            // 循环请求过来放入router数组的对象,当请求方法和路劲与对象一致时,执行回调handler方法
        for (var i = 1, len = router.length; i < len; i++) {
            if (
            (req.url === router[i].path || router[i].path === '*') &&
            (req.method === router[i].method || router[i].method === '*')
            ) {
            return router[i].handle && router[i].handle(req, res);
            }
        }
        return router[0].handle && router[0].handle(req, res);
        });
        return server.listen.apply(server, arguments);
    })
}
复制代码

总结

我们这里主要实现了express简单的搭建服务器和get请求方法的功能,满足了Hello World这个简单实例的要求。


以上所述就是小编给大家介绍的《# NodeJs 实战——原生 NodeJS 轻仿 Express 框架从需求到实现(一)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linked

Linked

Albert-Laszlo Barabasi / Plume / 2003-4-29 / GBP 11.24

A cocktail party. A terrorist cell. Ancient bacteria. An international conglomerate. All are networks, and all are a part of a surprising scientific revolution. Albert-L&aacuteszló Barab&a......一起来看看 《Linked》 这本书的介绍吧!

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具