前言
这几天为了熟悉 vue.js 框架, 还有 webpack 的使用, 就准备搭建一个发布和浏览 markdwon 的简单 WEB 应用. 原本是想着用 bash 脚本和 busybox 的 httpd 来作为后台服务, 但是 bash 脚本解析和生成 JSON 非常不方便, 而用 Java 语言写又觉得部署不方便, 所以就想到了正在用到的 Node.js, 于是就有了这篇博文.(文末有本文代码的 github 地址)
简单例子
首先, 从搭建最简单的 Hello world 开始, 建立以下目录, 文件和内容.
建立项目及运行
- project
- web-server
- + | - server.js
- server.js
- const http = require('http');
- http.createServer(function(request, response) {
- // 设置响应头
- response.writeHeader(200, {
- "Content-Type" : "text/plain"
- });
- // 响应主体为 "Hello world!"
- response.write("Hello world!");
- response.end();
- })
- // 设置监听端口为 9000
- .listen(9000);
现在, 在项目目录运行下面命令来执行 server.js , 浏览器地址栏中输入 localhost:9000 , 如果一切访问都正常, 浏览器就会显示 Hello world! .
node server.js
提示: 使用 ctrl+c 停止脚本运行.
至此一个简单例子就运行成功了, 下面来分析一下代码.
代码分析
首先, server.js 中引入了 Node.js 的 http 模块 , 它提供了非常底层 HTTP API 支持. 这里使用 createServer() 方法, 它返回一个 http.server 实例, 使用该实例的 listen() 方法来设置监听端口.
方法 createSever() 中填写的参数是一个函数, 该函数会作为回调函数自动添加到 request 事件 去, 其参数类型分别为 http.IncomingMessage 和 http.ServerResponse . 在回调函数体里, 利用 http.ServerResponse 的方法设置了响应头和响应主体, 最后以 end() 方法结束本次请求.
路由功能
上述的例子仅仅实现了简单请求响应功能, 现在增加路由的功能来健壮我们的 WEB 服务器. 现在, 修改为以下的目录, 文件和内容.
实现简单路由
- project
- web-server
- | - server.js
- + | - router.js
- server.js
- const http = require('http');
- const router = require('./router.js');
- function handleHello(request, response) {
- // 设置响应头
- response.writeHeader(200, {
- "Content-Type" : "text/plain"
- });
- // 响应主体为 "Hello world!"
- response.write("Hello world!");
- response.end();
- }
- http.createServer(function(request, response) {
- // 注册路径和其对应回调函数
- router.register(request, response, [
- {
- 'url': '/hello',
- 'handler': handleHello
- }
- ]);
- })
- // 设置监听端口为 9000
- .listen(9000);
- router.js
- const url = require('url');
- exports.register = function(request, response, mapping) {
- // 解析请求路径
- var pathName = url.parse(request.url).pathname;
- // 执行相应请求路径的回调函数
- for(let i = 0, len = mapping.length;i <len;i++) {
- if(mapping[i].url === pathName) {
- mapping[i].handler(request, response);
- return;
- }
- }
- // 请求路径不存在返回 404 页面
- response.writeHeader(404, {
- "Content-Type" : "text/html"
- });
- response.end(`
- <html>
- <head>
- <title>NOT FOUND</title>
- </head>
- <body>
- <h1>404 NOT FOUND</h1>
- </body>
- </html>
- `);
- }
现在, 再次执行 server.js 脚本, 接着浏览器访问 localhost:9000\hello 会得到 Hello world! 的结果, 而访问其他路径则会得到 404 页面.
这个功能的核心实现是在 router.js 中, 通过请求路径的解析, 然后根据预先注册好的 mapping 数组, 找到与之对应的路径并执行相应的回调函数.
静态资源请求
当前的路由功能只能实现回调函数的执行, 而一个 WEB 服务器应具有响应静态资源请求的能力, 接下我们继续来改造它. 现在, 保持 server.js 内容不变, 只改变 router.js 中的内容 (部分代码内容省略).
- route.js
- const url = require('url');
- const path = require('path');
- const fs = require('fs');
- function getErrorInfo(errorType) {
- // 省略代码
- }
- function writeErrorPage(response, errorType) {
- // 省略代码
- }
- exports.register = function(request, response, mapping) {
- // 解析请求路径
- var pathName = url.parse(request.url).pathname;
- // 执行相应请求路径的回调函数
- for(let i = 0, len = mapping.length;i < len;i++) {
- if(mapping[i].url === pathName) {
- mapping[i].handler(request, response);
- return;
- }
- }
- // 请求路径为文件返回文件内容
- var file = path.resolve(__dirname, '.' + pathName);
- fs.exists(file, function(exists) {
- // 请求路径不存在返回 404 页面
- if(!exists) {
- writeErrorPage(response, 'NOT_FOUND');
- }
- else {
- var stat = fs.statSync(file);
- // 请求路径为目录返回 403 页面
- if(stat.isDirectory()) {
- writeErrorPage(response, 'FORBIDDEN');
- }
- else {
- response.writeHeader(200, {
- "Content-Type" : "text/html"
- });
- response.end(
- fs.readFileSync(file, 'utf-8')
- );
- }
- }
- });
- }
将静态资源请求的行为置后的设计, 是为了保证回调函数一定能执行. 当静态资源不存在时, 应当返回不存在的错误, 同时也设置了禁止目录的访问的规则.
后话
现在, 只是实现了 WEB 服务器基本的功能, 它还有很大的改进空间. 我将项目开源到 github https://github.com/linzhehuang/web-server 上, 有兴趣的可以克隆下来. 希望对大家的学习有所帮助, 也希望大家多多支持脚本之家.
来源: http://www.jb51.net/article/145635.htm