拼多多版http-server

508 阅读4分钟

1 前言

不知从什么时候开始,本胖就喜欢用一个叫做http-server的中间件用来在启动一个静态的服务器以及解决跨域问题。

http-server 是一个简单的零配置命令行HTTP服务器, 基于 nodeJs。

现在,本胖要自己从零开始实现一个http-server中间件,暂且取名为lpp-http-server,用以致敬伟大的http-server。

2 所用npm包

一般来说写一个中间件会依赖其他的辅助包,lpp-http-server也不例外。这里本胖主要依赖了如下的npm包。

1.colors--用于在控制台输出五颜六色的命令
2.commander--提供了用户命令行输入和参数解析强大功能
3.http-proxy--转发请求
4.pug--模板

3 Server类

这个类是整个lpp-http-server的核心,作用就是实现一个静态服务器。主要包含了以下几个方法。

1.handleRequest——对请求的处理
2.sendDir--渲染目录信息
3.sendFile——获取文件信息
4.proxy——转发接口
5.sendError——发送错信息
6.start——启动

下面就来一一介绍这个方法

3.1 handleRequest

async handleRequest(req, res) {
    const { dir, proxyUrl } = this.config;
    let { pathname } = url.parse(req.url);
    pathname = decodeURIComponent(pathname);
    const fileUrl = path.join(dir, pathname);
    try {
      // 判断当前路径是文件 还是文件夹
      const statObj = await stat(fileUrl);
      if (statObj.isDirectory()) {
        // 文件夹则输出对应的目录
        this.sendDir(req, res, pathname, fileUrl);
      } else {
        // 文件则直接输出内容
        this.sendFile(req, res, statObj, fileUrl);
      }
    } catch (e) {
      // 转发请求
      if (proxyUrl) {
        this.proxy(req, res, proxyUrl);
      } else {
        this.sendError(req, res);
      }
    }
  }

上面是handleRequest方法的全部代码,可以看出,主要就是对请求的不同情况的处理,主要处理了以下几种情况。

1.请求的是文件地址
2.请求的是文件目录地址
3.请求的地址404

然后对这几种情况做了分别的处理。

3.2 sendDir

  // 获取文件夹目录
  async sendDir(req, res, pathname, fileUrl) {
    // 读取当前访问的目录下的所有内容 readdir 数组 把数组渲染回页面
    res.setHeader('Content-Type', 'text/html;charset=utf8')
    let dirs = await readdir(fileUrl);
    dirs = dirs.map(item => ({
      name: item,
      // 因为点击第二层时 需要带上第一层的路径,所有拼接上就ok了
      href: path.join(pathname, item)
    }))
    // 渲染template.html中需要填充的内容,name是当前文件目录,arr为当前文件夹下的目录数组
    const str = pug.render(this.template, {
      arr: dirs
    });
    // 响应中返回填充内容
    res.end(str);
  }

上面的sendDir方法是用来获取当前目录的,主要用了readdir方法来读取一个目录的内容,然后循环输出,这里用了pug当做html模板。pug文件的主要内容如下

doctype html
html
  head
    meta(charset="utf-8")
    meta(name="renderer", content="webkit")
    meta(http-equiv="X-UA-Compatible", content="IE=edge,chrome=1")
    title lvpp-http-server
  body
    block content
    div
    - for (var x = 0; x < arr.length; x++)
      div
        a(href=arr[x]['href'])= arr[x]['name']


3.3 sendFile

  // 获取文件信息
  sendFile(req, res, statObj, fileUrl) {
    // 管道读写操作,将fs.createReadStream(p)的内容写入到res
    // return fs.createReadStream(fileUrl).pipe(res);
    fs.readFile(fileUrl, 'utf-8', function (err, data) {
      if (err) {
        throw err;
      }
      res.end(data);
    });
  }

sendFile方法是用来获取文件信息的,当然最好是用createReadStream方法,他们的区别可以参考node官网。

3.4 proxy

  // 代理
  proxy(req, res, proxyUrl) {
    delete req.headers.host;
    var proxy = httpProxy.createProxyServer({});
    proxy.web(req, res, {
      target: proxyUrl
    });
  }

这里用了http-proxy包进行了转发,需要注意下面这行代码

delete req.headers.host;

一开始没有这行代码,请求转发是不行的。

3.5 start

  // 启动
  start() {
    const server = http.createServer(this.handleRequest.bind(this));
    server.listen(this.config.port, this.config.host, () => {
      console.log(`server start http://${this.config.host}:${colors.green(this.config.port)}`);

      // 代理
      if (this.config.proxyUrl) {
        console.log(colors.yellow('Unhandled requests will be served from: ' + this.config.proxyUrl));
      }
    });
  }

启动的方法主要是启动一个静态服务器http.createServer,然后对该服务器的一些配置。

4 启动脚本

这个脚本主要是对一些命令的操作,这里用的是commander这个模块。

#! /usr/bin/env node
// 告诉操作系统执行这个脚本的时候,调用 / usr / bin下的node解释器;

const Server = require('../src/index.js'); // 导入Server
const commander = require('commander'); // 导入命令行模块
const { version } = require('../package.json'); // 读取package.json的版本

// 配置命令行
commander
  .option('-p,--port <n>', 'config port') // 配置端口
  .option('-o,--host [value]', 'config hostname') // 配置主机名
  .option('-d,--dir [value]', 'config directory') // 配置访问目录
  .option('-P,--proxyUrl [value]', 'config proxy') // 配置转发地址
  .version(version, '-v,--version') // 展示版本
  .parse(process.argv);

const server = new Server(commander);
server.start();

在写好代码之后,我们想要的当然是执行一个自定义命令,就可以执行我们的包了,不用每次都是node ./bin/www.js。

这时候一步配置即可,找到整个项目的package.json文件,加入以下信息

"bin": {
    "lpp-http-server": "bin/www.js"
  },

上面这段命令的意思就是用lpp-http-server这个命令去代替执行node bin/www.js,写好命令后,还需要在整个目录下执行

npm link

然后你就会发现你在你电脑任何地方都可以执行lpp-http-server,生成一个静态服务器了,效果如下

本文完