美文网首页思科DevNet
Node学习之手写一个http-server

Node学习之手写一个http-server

作者: Mstian | 来源:发表于2020-11-16 19:50 被阅读0次

    模仿http-server创建一个静态服务器。
    http-server地址 https://www.npmjs.com/package/http-server

    功能:Http-server是一个轻量级的基于nodejs的http服务器,可以使任意一个目录成为服务器的目录。

    安装:npm i -g http-server

    使用:http-server(在想要使用的目录下直接使用命令)常用在测试Vue React等单页应用打包之后想要预览效果,缺不方便部署到服务器时。

    阅读该文章可以获取到的知识:

    1. node命令行交互使用的一些常用包(commander)
    2. npm link命令,将npm 模块链接到对应的运行项目中去,方便地对模块进行调试和测试。

    首先使用npm init -y创建一个新的项目


    项目目录图

    项目目录图如上所示。
    分别来介绍下每个目录的作用,在使用命令行命令时package.json中的bin属性指明了需要运行的文件:

    {
      "name": "node-http-server",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "bin": {
        "node-http-server": "./bin/www.js"
      },
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "chalk": "^4.1.0",
        "commander": "^6.2.0",
        "ejs": "^3.1.5",
        "mime": "^2.4.6",
        "mz": "^2.7.0",
        "url": "^0.11.0"
      }
    }
    

    然后在bin/www.js文件中编写交互代码

    #!/usr/bin/env node
    
    const { program } = require('commander');
    const json = require('../package.json');
    
    const Server = require('../Server');
    program.version(json.version)
    .option('-p --port <port>', '端口号')
    .option('-d --dir <dir>', '目录')
    .option('-h --host <host>', '主机')
    .parse(process.argv);
    
    program.on('--help', () => {
        console.log('');
        console.log('Example call:');
        console.log('  $ custom-help --help');
    });
    
    let config = {
        port: program.port,
        host: program.host,
        dir: program.dir
    }
    
    let server = new Server(config);
    server.start();
    

    在文件的顶部必须加上 #!/usr/bin/env node 作用是告诉操作系统执行这个脚本的时候,调用/usr/bin下的node解释器。具体的我也不是很了解操作系统这块,可以参考网上资料。https://www.cnblogs.com/qinmengjiao123-123/p/8503163.html

    然后使用commander模块,这是一个第三方模块参考:https://www.kancloud.cn/diaoyundexia/text/149934,commander模块用来制作命令行交互;一般命令行包含:命令,选项,参数。
    具体可以看npm官网或者其他资料,在这里就只粗略用了下。
    process.argv 属性会返回一个数组,其中包含当 Node.js 进程被启动时传入的命令行参数,通过program.parse方法会被直接解析,所以参数部分会根据前面的option中配置的命令直接解析。

    最后实例一个Server类,传递端口号,启动的目录,还有主机名,调用类上的start方法来启动服务。

    先看一下交互:
    在命令行输入命令 node-http-server --help

    交互效果
    
    const http = require('http');
    const fs = require('mz/fs');
    const mime = require('mime');
    const chalk = require('chalk');
    const path = require('path');
    const url = require('url');
    const ejs = require('ejs');
    let template = fs.readFileSync(path.join(__dirname, 'template.html'), 'utf-8');
    const baseConfig = {
        port: 3000,
        host: '127.0.0.1',
        dir: process.cwd()
    }
    class Server{
        constructor({port, host, dir}){
            this.port = port || baseConfig.port;
            this.host = host || baseConfig.host;
            this.dir = dir || baseConfig.dir;
            this.template = template;
        }
        start() {
            let server = http.createServer(this.sendRequest.bind(this));
            server.listen(this.port, this.host, () => {
                console.log(chalk.yellow(`服务启动, 服务路径是${this.dir}`));
                console.log(chalk.green(`http://${this.host}:${this.port}`));
            })
        }
        async sendRequest(req, res) {
            try{
                let {pathname} = url.parse(req.url);
                let currentPath = path.join(this.dir, pathname);
                let stats = await fs.stat(currentPath);
                if(stats.isDirectory()) {
                    let dirs = await fs.readdir(currentPath);
                    let datas = dirs.map((item, index) => {
                        return {
                            href: path.join(pathname, item),
                            content: item
                        }
                    })
                    let str = ejs.render(this.template, {data: datas});
                    res.setHeader('Content-type', 'text/html; charset-utf8');
                    res.end(str);
                } else {
                    this.responseData(req, res, currentPath);
                }
            }catch(e){
                console.log(e);
                this.sendError(req, res);
            }
        }
        responseData(req, res, currentPath) {
            res.setHeader('Content-type', mime.getType(currentPath) +'; charset=utf8');
            fs.createReadStream(currentPath).pipe(res);
        }
        sendError(req, res) {
            res.statusCode = 404;
            res.end('Not Found');       
        }
    }
    
    module.exports = Server;
    

    Server主要的逻辑就是考虑服务启动后服务的目录是一个文件还是文件夹,是文件直接读取展示,是文件夹用ejs模板直接渲染输出到页面,并且添加a链接可以点击进入。

    最终在命令行输入命令 node-http-server -p 3000 还可以传其他参数,这里只传了port。

    效果:


    预览效果1 预览效果2.png

    项目源代码地址:https://github.com/Mstian/node-static-server

    相关文章

      网友评论

        本文标题:Node学习之手写一个http-server

        本文链接:https://www.haomeiwen.com/subject/huvzbktx.html