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的无缝循环新闻列表插件
Mar 07 Javascript
jqplot通过ajax动态画折线图的方法及思路
Dec 08 Javascript
jquery禁用右键示例
Apr 28 Javascript
jquery.ajax之beforeSend方法使用介绍
Dec 08 Javascript
JavaScript中this详解
Sep 01 Javascript
javascript产生随机数方法汇总
Jan 25 Javascript
jQuery判断浏览器并动态调整select宽度的方法
Mar 02 Javascript
Vue实现自带的过滤器实例
Mar 09 Javascript
vue 实现剪裁图片并上传服务器功能
Mar 01 Javascript
vue toggle做一个点击切换class(实例讲解)
Mar 13 Javascript
JavaScript实现读取与输出XML文件数据的方法示例
Jun 05 Javascript
javascript实现贪吃蛇游戏(娱乐版)
Aug 17 Javascript
使用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+DBM的同学录程序(2)
2006/10/09 PHP
PHP的面试题集
2006/11/19 PHP
PHP实现用户认证及管理完全源码
2007/03/11 PHP
基于asp+ajax和数据库驱动的二级联动菜单
2010/05/06 PHP
PHP的魔术常量__METHOD__简介
2014/07/08 PHP
PHP微信刮刮卡 附微信接口
2016/07/22 PHP
yii2安装详细流程
2018/05/23 PHP
laravel 使用auth编写登录的方法
2019/09/30 PHP
JavaScript iframe的相互操作浅析
2009/10/14 Javascript
Prototype源码浅析 Enumerable部分之each方法
2012/01/16 Javascript
jQuery事件 delegate()使用方法介绍
2012/10/30 Javascript
javascript继承之为什么要继承
2012/11/10 Javascript
animate动画示例(泪奔的小孩)及stop和delay的使用
2013/05/06 Javascript
jQuery中animate()方法用法实例
2014/12/24 Javascript
javascript实现textarea中tab键的缩排处理方法
2015/06/26 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
2015/10/15 Javascript
jQuery插件ajaxFileUpload使用实例解析
2016/10/19 Javascript
Bootstrap超大屏幕的实现代码
2017/03/22 Javascript
JS组件系列之MVVM组件构建自己的Vue组件
2017/04/28 Javascript
详解vue模拟加载更多功能(数据追加)
2017/06/23 Javascript
vue项目使用axios发送请求让ajax请求头部携带cookie的方法
2018/09/26 Javascript
15分钟深入了解JS继承分类、原理与用法
2019/01/19 Javascript
Django 前后台的数据传递的方法
2017/08/08 Python
Python面向对象之类的封装操作示例
2019/06/08 Python
在Qt中正确的设置窗体的背景图片的几种方法总结
2019/06/19 Python
使用python将最新的测试报告以附件的形式发到指定邮箱
2019/09/20 Python
Python GUI编程学习笔记之tkinter中messagebox、filedialog控件用法详解
2020/03/30 Python
世界上最大的艺术和工艺用品商店:MisterArt.com
2018/07/13 全球购物
内部类的定义、种类以及优点
2013/10/16 面试题
一年级数学教学反思
2014/02/01 职场文书
共产党员批评与自我批评
2014/10/15 职场文书
学校运动会通讯稿
2015/07/18 职场文书
2021-4-5课程——SQL Server查询【3】
2021/04/05 SQL Server
python保存大型 .mat 数据文件报错超出 IO 限制的操作
2021/05/10 Python
PHP获取学生成绩的方法
2021/11/17 PHP