Node.js实战 建立简单的Web服务器


Posted in Javascript onMarch 08, 2012

前面一章,我们介绍了Node.js这个面向互联网服务的JavaScript服务器平台,同时Node.js的运行环境已经搭建起来,并通过两段HelloWorld程序验证了Node.js的基本功能。本章我们同样通过实战的演练,利用Node.js建立一个简单的Web服务器。

Node.js实战 建立简单的Web服务器

    如果你熟悉.NET或其他类似平台的Web开发,你可能会像,建立一个Web服务器有什么,在Visual Studio中建立一个Web工程,点击运行即可。事实的确是这样,但请不要忘记,这样的代价是,比如果说,你是用.NET开发Web应用,你就使用了完整的IIS作为你的Web服务器基础,这样当你的应用发布时就只能用IIS了。而如果使用独立服务器(利用System.Web.Hosting自己构建的话),你则必须处理各种HttpListener和相应线程,则会比较麻烦,毕竟.NET不是专注于面向Web的。Node.js在这方面提供了方便且可定制的途径,你可以在其基础上构建精巧且完全面向你应用的服务平台。

一、建立简单的Web服务器涉及到Node.js的一些基本知识点:

1、请求模块
在Node.js中,系统提供了许多有用的模块(当然你也可以用JavaScript编写自己的模块,以后的章节我们将详细讲解),如http、url等。模块封装特定的功能,提供相应的方法或属性,要使用这些模块,需要先请求模块获得其操作对象。

例如要使用系统的http模块,可以这样写:

var libHttp = require('http'); //请求HTTP协议模块

这样,以后的程序将可以通过变量libHttp访问http模块的功能。本章例程中使用了以下系统模块:
http:封装http协议的服务器和客户端实现;
url:封装对url的解析和处理;
fs:封装对文件系统操作的功能;
path:封装对路径的解析功能。
有了这些模块,我们就可以站在巨人的肩膀上构建自己的应用。
2、控制台
为了更好的观察程序的运行,方便在异常时查看错误,可以通便变量console使用控制台的功能。
console.log('这是一段日志信息'); 
计时并在控制台上输出计时信息: 
//开始计时 
console.timeEnd('计时器1'); //开始名称为“计时器1”的计时器 
... 
... 
... 
//结束计时,并输出到控制台 
console.timeEnd('计时器1'); //结束称为“计时器1”的计时器并输出

3、定义函数
在Node.js中定义函数的办法与普通JavaScript中完全相同,不过我们推荐的写法如下,即使用一个变量为函数命名,这样可以比较方便明确的将函数作为参数传递给其他函数:
//定义一个名为showErr的函数 
var showErr=function(msg){ 
var inf="错误!"+msg; 
console.log(inf+msg); 
return msg; 
}

4、创建Web服务器并侦听访问请求
创建Web服务器最重要的是提供Web请求的响应函数,它有两个参数,第一个代表客户端请求的信息,另一个代表将要返回给客户端的信息。在响应函数中应解析请求信息,依据请求,组装返后内容。
//请求模块 
var libHttp = require('http'); //HTTP协议模块 
//Web服务器主函数,解析请求,返回Web内容 
var funWebSvr = function (req, res){ 
res.writeHead(200, {'Content-Type': 'text/html'}); 
res.write('<html><body>'); 
res.write('<h1>*** Node.js ***</h1>'); 
res.write('<h2>Hello!</h2>'); 
res.end('</body></html>'); 
} 
//创建一个http服务器 
var webSvr=libHttp.createServer(funWebSvr); 
//开始侦听8124端口 
webSvr.listen(8124);

5、解析Web请求
对于简单的Web网页访问请求,重要的信息包含在请求信息参数的url里,我们可以使用url解析模块解析url中的访问路径,并利用path模块,将访问路径组装为要访问的实际文件路径用于返回。
var reqUrl=req.url; //获取请求的url 
//向控制台输出请求的路径 
console.log(reqUrl); 
//使用url解析模块获取url中的路径名 
var pathName = libUrl.parse(reqUrl).pathname; 
//使用path模块获取路径名中的扩展名 
if (libPath.extname(pathName)=="") { 
//如果路径没有扩展名 
pathName+="/"; //指定访问目录 
} 
if (pathName.charAt(pathName.length-1)=="/"){ 
//如果访问目录 
pathName+="index.html"; //指定为默认网页 
} 
//使用路径解析模块,组装实际文件路径 
var filePath = libPath.join("./WebRoot",pathName);

6、设置返回头
由于是Web请求,需要在返回内容中包含http返回头,这里重点是依据要访问的文件路径的文件扩展名,设置http返回头的内容类型。
var contentType=""; 
//使用路径解析模块获取文件扩展名 
var ext=libPath.extname(filePath); 
switch(ext){ 
case ".html": 
contentType= "text/html"; 
break; 
case ".js": 
contentType="text/javascript"; 
break; 
... 
... 
default: 
contentType="application/octet-stream"; 
} 
//在返回头中写入内容类型 
res.writeHead(200, {"Content-Type": contentType });

7、向返回对象中写入访问的文件内容
有了需要访问的文件实际路径,有了文件对应的内容类型,就可以利用fs文件系统模块读取文件流并返回给客户端。
//判断文件是否存在 
libPath.exists(filePath,function(exists){ 
if(exists){//文件存在 
//在返回头中写入内容类型 
res.writeHead(200, {"Content-Type": funGetContentType(filePath) }); 
//创建只读流用于返回 
var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null}); 
//指定如果流读取错误,返回404错误 
stream.on("error", function() { 
res.writeHead(404); 
res.end("<h1>404 Read Error</h1>"); 
}); 
//连接文件流和http返回流的管道,用于返回实际Web内容 
stream.pipe(res); 
} 
else { //文件不存在 
//返回404错误 
res.writeHead(404, {"Content-Type": "text/html"}); 
res.end("<h1>404 Not Found</h1>"); 
} 
});

二、测试及运行
1、完整源码
以下100行左右的JavaScript就是建立这样一个简单web服务器的全部源码:
//------------------------------------------------ 
//WebSvr.js 
// 一个演示Web服务器 
//------------------------------------------------ 
//开始服务启动计时器 
console.time('[WebSvr][Start]'); 
//请求模块 
var libHttp = require('http'); //HTTP协议模块 
var libUrl=require('url'); //URL解析模块 
var libFs = require("fs"); //文件系统模块 
var libPath = require("path"); //路径解析模块 
//依据路径获取返回内容类型字符串,用于http返回头 
var funGetContentType=function(filePath){ 
var contentType=""; 
//使用路径解析模块获取文件扩展名 
var ext=libPath.extname(filePath); 
switch(ext){ 
case ".html": 
contentType= "text/html"; 
break; 
case ".js": 
contentType="text/javascript"; 
break; 
case ".css": 
contentType="text/css"; 
break; 
case ".gif": 
contentType="image/gif"; 
break; 
case ".jpg": 
contentType="image/jpeg"; 
break; 
case ".png": 
contentType="image/png"; 
break; 
case ".ico": 
contentType="image/icon"; 
break; 
default: 
contentType="application/octet-stream"; 
} 
return contentType; //返回内容类型字符串 
} 
//Web服务器主函数,解析请求,返回Web内容 
var funWebSvr = function (req, res){ 
var reqUrl=req.url; //获取请求的url 
//向控制台输出请求的路径 
console.log(reqUrl); 
//使用url解析模块获取url中的路径名 
var pathName = libUrl.parse(reqUrl).pathname; 
if (libPath.extname(pathName)=="") { 
//如果路径没有扩展名 
pathName+="/"; //指定访问目录 
} 
if (pathName.charAt(pathName.length-1)=="/"){ 
//如果访问目录 
pathName+="index.html"; //指定为默认网页 
} 
//使用路径解析模块,组装实际文件路径 
var filePath = libPath.join("./WebRoot",pathName); 
//判断文件是否存在 
libPath.exists(filePath,function(exists){ 
if(exists){//文件存在 
//在返回头中写入内容类型 
res.writeHead(200, {"Content-Type": funGetContentType(filePath) }); 
//创建只读流用于返回 
var stream = libFs.createReadStream(filePath, {flags : "r", encoding : null}); 
//指定如果流读取错误,返回404错误 
stream.on("error", function() { 
res.writeHead(404); 
res.end("<h1>404 Read Error</h1>"); 
}); 
//连接文件流和http返回流的管道,用于返回实际Web内容 
stream.pipe(res); 
} 
else { //文件不存在 
//返回404错误 
res.writeHead(404, {"Content-Type": "text/html"}); 
res.end("<h1>404 Not Found</h1>"); 
} 
}); 
} 
//创建一个http服务器 
var webSvr=libHttp.createServer(funWebSvr); 
//指定服务器错误事件响应 
webSvr.on("error", function(error) { 
console.log(error); //在控制台中输出错误信息 
}); 
//开始侦听8124端口 
webSvr.listen(8124,function(){ 
//向控制台输出服务启动的信息 
console.log('[WebSvr][Start] running at http://127.0.0.1:8124/'); 
//结束服务启动计时器并输出 
console.timeEnd('[WebSvr][Start]'); 
});

2、资源目录

    既然要建立Web服务器,我们需要创建一个WebRoot目录来存放实际的网页和图片资源,“WebRoot”的目录名在以上源码中被用于组装实际文件路径。

Node.js实战 建立简单的Web服务器

 

3、运行并测试

    在命令行中输入:

node.exe WebSvr.js

   我们的Web服务器就运行起来了,这时,可以通过浏览器对其进行访问,运行效果如下:

Node.js实战 建立简单的Web服务器

 

 后记

    利用Node.js我们可以方便建立相对独立的Web服务器,其事件驱动的特性避免繁琐的线程保护,其基础模块更降低了开发难度。本章建立的Web服务器只是一个简单的样本,没有过多的考虑模块化、安全性等问题,但可以从中掌握Node.js开发的一些基本的知识。
作者:汪峰 www.otlive.cn

Javascript 相关文章推荐
读jQuery之七 判断点击了鼠标哪个键的代码
Jun 21 Javascript
JS 加入收藏夹的代码(主流浏览器通用)
May 13 Javascript
jquery中通过父级查找进行定位示例
Jun 28 Javascript
分享9点个人认为比较重要的javascript 编程技巧
Apr 27 Javascript
JavaScript 模块的循环加载实现方法
Dec 13 Javascript
JavaScript function函数种类详解
Feb 22 Javascript
jquery 标签 隔若干行加空白或者加虚线的方法
Dec 07 Javascript
你有必要知道的10个JavaScript难点
Jul 25 Javascript
温故知新——JavaScript中的字符串连接问题最全总结(推荐)
Aug 21 Javascript
微信小程序分享功能之按钮button 边框隐藏和点击隐藏
Jun 14 Javascript
一个Java程序猿眼中的前后端分离以及Vue.js入门(推荐)
Apr 19 Javascript
Vue项目打包部署到apache服务器的方法步骤
Feb 01 Vue.js
使用UglifyJS合并/压缩JavaScript的方法
Mar 07 #Javascript
Uglifyjs(JS代码优化工具)入门 安装使用
Apr 13 #Javascript
node.js 一个简单的页面输出实现代码
Mar 07 #Javascript
服务器端的JavaScript脚本 Node.js 使用入门
Mar 07 #Javascript
JavaScript高级程序设计 读书笔记之十一 内置对象Global
Mar 07 #Javascript
拥抱模块化的JavaScript
Mar 07 #Javascript
那些年,我还在学习jquery 学习笔记
Mar 05 #Javascript
You might like
PHP不用第三变量交换2个变量的值的解决方法
2013/06/02 PHP
php发送post请求的三种方法
2014/02/11 PHP
PHP编写RESTful接口
2016/02/23 PHP
PHP开发之归档格式phar文件概念与用法详解【创建,使用,解包还原提取】
2017/11/17 PHP
Jquery AJAX 框架的使用方法
2009/11/03 Javascript
UpdatePanel和Jquery冲突的解决方法
2013/04/01 Javascript
Asp.Net alert弹出提示信息的几种方法总结
2014/01/29 Javascript
js图片预加载示例
2014/04/30 Javascript
使用Javascript简单实现图片无缝滚动
2014/12/05 Javascript
jQuery实现带有动画效果的回到顶部和底部代码
2015/11/04 Javascript
javascript入门教程基础篇
2015/11/16 Javascript
JS控制HTML元素的显示和隐藏的两种方法
2016/09/27 Javascript
JS和canvas实现俄罗斯方块
2017/03/14 Javascript
微信小程序 本地数据存储实例详解
2017/04/13 Javascript
vue+mousemove实现鼠标拖动功能(拖动过快失效问题解决方法)
2018/08/24 Javascript
微信小程序生成二维码的示例代码
2019/03/29 Javascript
jquery实现聊天机器人
2020/02/08 jQuery
微信小程序webSocket的使用方法
2020/02/20 Javascript
vue实现的多页面项目如何优化打包的步骤详解
2020/07/19 Javascript
Python设计模式编程中Adapter适配器模式的使用实例
2016/03/02 Python
Django URL传递参数的方法总结
2016/08/28 Python
Python 函数基础知识汇总
2018/03/09 Python
python多线程调用exit无法退出的解决方法
2019/02/18 Python
【python】matplotlib动态显示详解
2019/04/11 Python
python 检查数据中是否有缺失值,删除缺失值的方式
2019/12/02 Python
python 实现多维数组(array)排序
2020/02/28 Python
JD Sports瑞典:英国领先的运动时尚商店
2018/01/28 全球购物
印度购买眼镜和太阳镜网站:Coolwinks
2018/09/26 全球购物
澳大利亚家具商店:Freedom
2020/12/17 全球购物
临床医学专业个人的自我评价
2013/09/27 职场文书
信息技术教学反思
2014/02/12 职场文书
项目经理聘任书
2014/03/29 职场文书
2015年检验科工作总结
2015/04/27 职场文书
公安机关起诉意见书
2015/05/20 职场文书
货款欠条范本
2015/07/03 职场文书
详解MySQL的主键查询为什么这么快
2022/04/03 MySQL