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,生成一个静态服务器了,效果如下



本文完