node.js如何搭建web服务器
今天测试移动端页面使用谷歌浏览器的手机模拟器测试页面效果时发现和手机看的效果有差异,因此想在本地搭建一个简单的web服务器,通过谷歌浏览器安装一个在线二维码生成工具,在浏览器工具栏上显示,点击生成当前url的二维码,通过手机微信扫聊二维码浏览本地制作的页面,这样非常方便移动端页面的查看调试,由于效果需要不断查看修改后的效果,在手机微信浏览器右上角下拉菜单里有刷新选项,通过刷新查看效果,还好公司有wifi不用费自己的流量。那么接着问题来了,如何高效的搭建一个操作简单方便的web服务器呢,马上想到了node.js帮助你实现web服务器。通过搜集资料整理,安装node环境,配置httpserver.js 成功搭建了web服务器。 下面分享一下node.js搭建的web服务器的案例和相关node的知识点。
js编写后端脚本语言这个同后端数据交互,大大减少了前端工程师学习服务端脚本的学习成本, 先了解一下简单的web服务器所涉及到的node的一些基本知识点:(网络资料整理)
一、node的一些基本知识点:
1、请求模块
在node中,系统提供了很多有用的模块,如http、url等。我们也可以编写自己的模块。模块封装了特定的功能,提供相应的方法和属性,要使用这些模块需要先请求模块,获取操作对象。
例如使用系统的http模块,可以这样写:
var libHttp = require('http'); //请求并获取http模块操作对象
这样,以后的程序将可以通过变量libhttp访问http模块的功能。
我们搭建简单服务器所用的到模块有以下几种:
http:封装http协议的服务器和客户端实现;
url:封装对url的解析和处理;
fs:封装对文件系统操作的功能;
path:封装对路径的解析功能
有了这些模块,我们就可以站在巨人肩膀上搭建自己的应用。
2、控制台
为了更好的观察程序的运行,方便在异常时查看错误,可以通过console控制台功能。
console.log('日志');
//计时开始
console.timeEnd('计时器1');
...
...
console.timeEnd('计时器1');
3、函数定义
在node中定义函数的办法与普通js完全相同,不过推荐使用变量+匿名函数的形式,这样可以比较方明确的将函数作为参数传递给其他函数。
var funWebSvr = function (request, response){...}
4、创建web服务器并监听访问请求
创建web服务器最重要的是提供web请求的响应函数,它有两个参数,的一个代表客户端请求信息,另一个代表要返回给客户端的信息。在响应函数中应解析请求信息,依据请求,组装返回内容
var port = 8124;//端口
var libHttp = require('http'); //请求并获取http模块操作对象
var funWebSvr = function (request, response){
response.writeHead(200, {'Content-Type': 'text/html'});
response.write('<html><body>');
response.write('<h1>*** Node.js ***</h1>');
response.write('<h2>Hello!</h2>');
response.end('</body></html>');
};
var webserver = libhttp.createServer(funWebSvr);
//监听端口
webserver.listen(port);
5、解析web请求
对于简单的web网页请求,重要的信息包含在请求信息参数的url中,我们可以使用url解析模块解析url中的访问路径,并利用path模块,将访问路径组装为要访问的实际文件路径用于返回。
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("./web/",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>");
}
});
以上代码按照逻辑组装后,保存为http.js。
//完整代码
//creat http server
//------------------------------------------------
//WebSvr.js
// 一个演示Web服务器
//------------------------------------------------
//开始服务启动计时器
console.time('[WebSvr][Start]');
//请求模块
var port = 8124;
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("./web/",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(port,function(){
//向控制台输出服务启动的信息
console.log('[WebSvr][Start] running at http://127.0.0.1:8124/');
//结束服务启动计时器并输出
console.timeEnd('[WebSvr][end]');
});
资源目录
既然要建立Web服务器,我们需要创建一个web目录来存放实际的网页和图片资源,"web"的目录名在以上源码中被用于组装实际文件路径。
运行并测试
在命令行中输入:
node.exe http.js
var http = require('http');
var url = require('url');
var fs = require('fs');
var path = require('path');
//配置
var config = {
port: 80,
denyAccess: ['./httpserver.js', './src/requirecache.js'],
localIPs: ['192.168.88.43'],
srcpath: '/src'
};
静态资源在node.js里的意思是不变的,如图片、前端js、css、html页面等。
动态资源我们一般指aspx页面,ashx页面,asp页面,jsp页面,php页面等,而node.js里其实没动态资源这一说,它对请求的处理都是由回调方法完成的,在我实现的httserver里,借鉴了ashx的写法,把处理请求的js文件看作动态资源。
首先实现一个处理静态资源的函数,其实就是对本地文件的读取操作,这个方法已满足了上面说的静态资源的处理。
//处理静态资源
function staticResHandler(localPath, ext, response) {
fs.readFile(localPath,
"binary"
, function (error, file) {
if
(error) {
response.writeHead(500, {
"Content-Type"
:
"text/plain"
});
response.end(
"Server Error:"
+ error);
}
else
{
response.writeHead(200, {
"Content-Type"
: getContentTypeByExt(ext) });
response.end(file,
"binary"
);
}
});
}
//开始HTTP服务器
http.createServer(processRequestRoute).listen(config.port);
console.log("Server has started. port:"+config.port);
动态资源
而动态资源肯定不能一个方法搞定,就像你的网站有register.aspx、login.aspx等等,都需要你自己来写,在我的httpserver里,每个处理请求的js模块都导出processRequest(request,response)即可,比如实现一个register.js(只输出字符串register)
1
2
3
4
|
exports.processRequest = function (request, response) { response.writeHead(200, { 'Content-Type' : 'text/plain' }); resp.end( "register" ); } |
现在当请求到来时,我们要做的就是决定怎么处理,即路由。
访问http://192.168.88.43/index.html 就是访问 web根目录index.html;
访问http://192.168.88.43/js/httpserver.js 就是访问 web根目录\js\httpserver.js;
192.168.88.43是我本地局域网的IP地址,不用loachost/127.0.0.1 这个是为了局域网的其他人也能通过这个地址访问。
3.运行web服务器
运行了node 运行了node httpserver.js后,访问http://192.168.88.43/vip.html(这个是web根目录下的文件)
页面可以正常访问,ok,搞定。
httpserver.js源码 :
[codesyntax lang="php"]
var http = require('http'); var url = require('url'); var fs = require('fs'); var path = require('path'); //配置 var config = { port: 80, denyAccess: ['./httpserver.js', './src/requirecache.js'], localIPs: ['192.168.88.43'], srcpath: '/src' }; //开始HTTP服务器 http.createServer(processRequestRoute).listen(config.port); console.log("Server has started. port:"+config.port); //路由URL function processRequestRoute(request, response) { var pathname = url.parse(request.url).pathname; if (pathname === '/') { pathname = "/index.html"; //默认页面 } var ext = path.extname(pathname); var localPath = ''; //本地相对路径 var staticres = false; //是否是静态资源 if (ext.length > 0) { localPath = '.' + pathname; staticRes = true; } else { localPath = '.' + config.srcpath + pathname + '.js'; staticRes = false; } //禁止远程访问 if (config.denyAccess && config.denyAccess.length > 0) { var islocal = false; var remoteAddress = request.connection.remoteAddress; for (var j = 0; j < config.localIPs.length; j++) { if (remoteAddress === config.localIPs[j]) { islocal = true; break; } } if (!islocal) { for (var i = 0; i < config.denyAccess.length; i++) { if (localPath === config.denyAccess[i]) { response.writeHead(403, { 'Content-Type': 'text/plain' }); response.end('403:Deny access to this page'); return; } } } } //禁止访问后端js if (staticRes && localPath.indexOf(config.srcpath) >= 0) { response.writeHead(403, { 'Content-Type': 'text/plain' }); response.end('403:Deny access to this page'); return; } fs.exists(localPath, function (exists) { if (exists) { if (staticRes) { staticResHandler(localPath, ext, response); //静态资源 } else { try { var handler = require(localPath); if (handler.processRequest && typeof handler.processRequest === 'function') { handler.processRequest(request, response); //动态资源 } else { response.writeHead(404, { 'Content-Type': 'text/plain' }); response.end('404:Handle Not found'); } } catch (exception) { console.log('error::url:' + request.url + 'msg:' + exception); response.writeHead(500, { "Content-Type": "text/plain" }); response.end("Server Error:" + exception); } } } else { //资源不存在 response.writeHead(404, { 'Content-Type': 'text/plain' }); response.end('404:File Not found'); } }); } //处理静态资源 function staticResHandler(localPath, ext, response) { fs.readFile(localPath, "binary", function (error, file) { if (error) { response.writeHead(500, { "Content-Type": "text/plain" }); response.end("Server Error:" + error); } else { response.writeHead(200, { "Content-Type": getContentTypeByExt(ext) }); response.end(file, "binary"); } }); } //得到ContentType function getContentTypeByExt(ext) { ext = ext.toLowerCase(); if (ext === '.htm' || ext === '.html') return 'text/html'; else if (ext === '.js') return 'application/x-javascript'; else if (ext === '.css') return 'text/css'; else if (ext === '.jpe' || ext === '.jpeg' || ext === '.jpg') return 'image/jpeg'; else if (ext === '.png') return 'image/png'; else if (ext === '.ico') return 'image/x-icon'; else if (ext === '.zip') return 'application/zip'; else if (ext === '.doc') return 'application/msword'; else return 'text/plain'; }
[/codesyntax]