一、使用Node实现基本Apache的功能

在上一篇笔记中,我们提到如果打开一个文件需要进行一次url判断是繁琐的,我们希望我们的Node具有类似Apache这种web服务器的一个功能:将文件放到www这个文件夹下,我们只要输入对应的文件地址就能访问到相应文件。

之前我们是对每一个文件进行request.url判断,再处理相应的Content-Type,这样比较麻烦,现在我们对url进行统一处理即可:

  • 要模拟www文件的功能,我们先将我们的项目放到www文件夹下:

  • 统一处理访问www文件夹下文件的url
var http = require('http');
var fs = require('fs'); var server = http.createServer();
server.listen(8000);
console.log('server running'); server.on('request',function(request,response){
var url = request.url;
var rootDir = 'D:/Users/Administrator/Desktop/Node/www';//www文件夹路径
var filePath = '/index.html';//文件地址,默认打开index.html
if(url !== '/'){
filePath = url;
} //根据url打开文件
fs.readFile(rootDir+filePath,function(err,data){
if(err){
return response.end('404,Not Fond!');
}
response.end(data);
});
});

原理是一样的,只不过进行了统一处理;浏览器其实会自动判断data的类型来处理文件,所以很多时候不用设置Content-Type。

二、案例:使用Node实现显示文件目录的效果

在浏览器中输入目录地址我们可以看到这样的效果:

现在使用Node来实现这个效果,我们需要知道fs模块的一个方法:readdir()即读取目录:

var http = require('http');
var fs = require('fs'); var server = http.createServer();
server.listen(8000);
console.log('server running'); server.on('request',function(request,response){
var url = request.url;
var rootDir = 'D:/Users/Administrator/Desktop/Node/www';//www文件夹路径 //读取目录
fs.readdir(rootDir,function(err,data){
if(err){
return response.end('404,Not Fond!');
}
console.log(data);
});
});

可以发现,读取的数据data是以数组形式显示出对于路径下的文件(夹)名。

现在我们只需要将这些数据显示在页面中即可,怎么将数据变成网页效果呢,一般情况下我们可以使用模块代码来提高效率,类似这样的逻辑:

其实就是想数据填到对应的位置,而不用去关心相应的页面效果,页面效果交给模板代码来处理,我们只需要关注两件事:获取数据data;将data填入模板代码中并使用。

(这里的模板代码我们自己随便写一个好了)

1.编写模板代码


<!DOCTYPE html>
<html>
<head>
<title>DirShow</title>
</head>
<body>
<h1>XXXURL的索引</h1>
<div style="display: block;">
<a href="#">
<span>[上级目录]</span>
</a>
</div>
<table>
<thead>
<tr>
<th>名称</th>
<th class="detailsColumn">
大小
</th>
<th class="detailsColumn">
修改日期
</th>
</tr>
</thead>
<tbody id="tbody">
<tr>
<td><a href="#">css/</a></td>
<td>1k</td>
<td>2018/11/5 上午10:50:48</td>
</tr>
</tbody>
</table>
</body>
</html>

效果如上,我们把框起来的部分使用我们读取的data替换掉就好了:

2.获取数据

上面已经获取过,使用readdir()方法来获取:

3.将数据填充到模板中并使用

我们发现模板代码其实就是字符串而已,我们将数据填充进去,其实就是进行简单的字符串替换工作即可:

var http = require('http');
var fs = require('fs'); var server = http.createServer();
server.listen(8000);
console.log('server running'); server.on('request',function(request,response){
var url = request.url;
var rootDir = 'D:/Users/Administrator/Desktop/Node/www'+url;//根据请求路径显示 //创建模板代码
var contentTemplate = `
<tr>
<td><a href="#">filename</a></td>
<td>1k</td>
<td>2018/11/5 上午10:50:48</td>
</tr>
`;
var content = '';
//获取数据
fs.readdir(rootDir,function(err,data){
if(err){
return response.end('404,Not Fond!');
}
//将数据填充到模板代码中---字符串替换操作
data.forEach(function(value,index){
content += contentTemplate.replace('filename',value);
//更新和使用模板代码
});
var templateStr = `
<h1>${rootDir}的索引</h1>
<div style="display: block;">
<a href="#">
<span>[上级目录]</span>
</a>
</div>
<table>
<thead>
<tr>
<th>名称</th>
<th class="detailsColumn">
大小
</th>
<th class="detailsColumn">
修改日期
</th>
</tr>
</thead>
<tbody id="tbody">
${content}
</tbody>
</table>
`;
response.setHeader('Content-Type','text/html;charset=utf-8');
response.end(templateStr);
});
});

效果如下:

三、模板引擎

模板引擎是什么这里就不过多介绍了,从上面的案例中我们已近手动实现了模板引擎要做的事,处理字符串,更准确地说应该是将数据填充到模板代码这一字符串操作过程我们可以通过模板引擎来完成。

模板引擎有诸多优点,这里不再赘述,我们在使用中感受。比如art-yemplate模板引擎。官网地址:art-yemplate

1.安装art-template

既然使用了Node.js我们就尽量使用npm来安装,新建一个文件夹code来保存代码:

2.在浏览器中使用art-template

<!DOCTYPE html>
<html>
<head>
<title>Demo</title>
<mata charset=utf-8>
<script type="text/javascript" src="./code/node_modules/art-template/lib/template-web.js"></script>
</head>
<body>
<!--显示位置-->
<p id="show"></p> <!--模板代码-->
<script type="text/html" id='demo'>
大家好,我叫{{ name }},
我喜欢{{each hobbies}}{{$value}} {{/each}}
</script> <script type="text/javascript">
//数据
var data = {
name: '张三',
hobbies: ['看书','打游戏','写代码']
};
//使用template方法将数据填充到模板中
var html = template('demo',data);
//将模板显示到页面上
window.document.getElementById('show').innerHTML = html;
</script>
</body>
</html>

注意几点:

  • 记得引用template-web.js文件
  • 相关格式要求和api查看官网文档即可
  • 模板引擎不关心格式以及内容,只是将{{}}表达式中的值进行数据填充,和我们手动replace处理字符串的操作是一样的。
  • 模板引擎还有其他好处,比如根据语句来处理数据,内部引用等。比我们手动处理字符串要方便很多,算是一个介绍代码工作的工具,且运行速度很快,渲染接近原生js。(有的模板引擎稍慢)

3.在Node.js中使用art-template

我们直接在上面的案例上进行改动即可:

var http = require('http');
var fs = require('fs');
var template = require('art-template');//启用art-template模板引擎 var server = http.createServer();
server.listen(8000);
console.log('server running'); server.on('request',function(request,response){
var url = request.url;
var rootDir = 'D:/Users/Administrator/Desktop/Node/www'+url;//根据请求路径显示 //创建模板代码
var templateStr = `
<h1>{{dir}}的索引</h1>
<div style="display: block;">
<a href="#">
<span>[上级目录]</span>
</a>
</div>
<table>
<thead>
<tr>
<th>名称</th>
<th class="detailsColumn">
大小
</th>
<th class="detailsColumn">
修改日期
</th>
</tr>
</thead>
<tbody id="tbody">
{{each data}}
<tr>
<td><a href="#">{{$value}}</a></td>
<td>1k</td>
<td>2018/11/5 上午10:50:48</td>
</tr>
{{/each}}
</tbody>
</table>
`;
//获取数据
fs.readdir(rootDir,function(err,data){
if(err){
return response.end('404,Not Fond!');
}
//使用模板引擎填充数据
var htmStr = template.render(templateStr,{
dir:rootDir,
data:data
});
response.setHeader('Content-Type','text/html;charset=utf-8');
response.end(htmStr);
});
});

值得注意的点:

  • 在Node.js中使用require()来加载模板引擎
  • 使用模板引擎的render()方法来处理模板代码(在浏览器在中是template()方法)
  • 在浏览器中是使用id作为参数,在Node.js中直接使用模板代码作为参数即可。
  • 注意模板代码要符合使用的模板引擎的规范。${value}是es6中模板字符串的变量表达式,而{{}}是art-template模板引擎的表达式。

四、案例:留言板

需求:使用Node.js实现留言板功能,即在如下页面输入评论,首页显示评论,相关html和css代码如下:

1.准备工作

目录结构如下:

html代码(views文件夹下)

js/css/img(public文件夹下,其实只用到了bootstrap.css,可自行网上下载引用)

模板引擎代码(node_modules文件夹下)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>留言本</title>
<link rel="stylesheet" href="../public/css/bootstrap.css">
</head>
<body>
<div class="header container">
<div class="page-header">
<h1>Example page header <small>Subtext for header</small></h1>
<a class="btn btn-success" href="/post">发表留言</a>
</div>
</div>
<div class="comments container">
<ul class="list-group">
{{each comments}}
<li class="list-group-item">
<span style="color: green">{{ $value.name }}:</span>{{ $value.message }}
<span class="pull-right">{{ $value.dateTime }}</span>
</li>
{{/each}}
</ul>
</div>
</body>
</html>

post.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="../public/css/bootstrap.css">
</head>
<body>
<div class="header container">
<div class="page-header">
<h1><a href="/">首页</a> <small>发表评论</small></h1>
</div>
</div>
<div class="comments container">
<form action="/addComment" method="post">
<div class="form-group">
<label for="input_name">你的大名</label>
<input type="text" class="form-control" required minlength="2" maxlength="10" id="input_name" name="name" placeholder="请写入你的姓名">
</div>
<div class="form-group">
<label for="textarea_message">留言内容</label>
<textarea class="form-control" name="message" id="textarea_message" cols="30" rows="10" required minlength="1" maxlength="20"></textarea>
</div>
<button type="submit" class="btn btn-default">发表</button>
</form>
</div>
</body>
</html>

404.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<h1>抱歉! 您访问的页面失联啦...</h1>
</body>
</html>

2.编写app.js文件(post提交)

var http = require('http');
var fs = require('fs');
var template = require('art-template'); var comments = []; http.createServer(function(req,res){
var url = req.url;
var rootDir = 'D:/Users/Administrator/Desktop/Node/feadback';
var filePath = '/views/index.html';
if(url === '/addComment'){
//request的data事件
req.on('data',function(postdata){
//postdata是二进制数据,postdata.toString()返回的数据编码方式任然有问题,所以需要解码
var decodedata = decodeURIComponent(postdata.toString());//对表单数据进行解码--->name=xxx&massage=xxx
var data = {},key,value,arr;
decodedata.split('&').forEach(function(v){
arr = v.split('=');
key = arr[0];
value = arr[1] || '';
data[key] = value;
});
data['dateTime'] = new Date().toLocaleString();
comments.unshift(data);
});
url = '/';
}
if(url !== '/'){
filePath = url;
}
if(url === '/post'){
filePath = '/views/post.html';
}
if(url !== '/'){
fs.readFile(rootDir+filePath,function(err,data){
if(err){
fs.readFile(rootDir+'/views/404.html',function(err1,data1){
if(err1)
return res.end('404,Not Fond.');
return res.end(data1);
})
return;
}
res.end(data);
});
}else{
//index.html需要进行模板渲染
fs.readFile(rootDir+filePath,function(err,data){
if(err){
fs.readFile(rootDir+'/views/404.html',function(err1,data1){
if(err1)
return res.end('404,Not Fond.');
return res.end(data1);
})
return;
}
var html = template.render(data.toString(),{comments:comments});
res.end(html);
});
}
}).listen(8000);
console.log('server running...');

值得注意的一些细节:

  • 注意fileRead()读取到的数据是二进制数据,要当做模板代码需要使用toString()进行处理。
  • 我们使用的是post提交,那么表单数据就不会显示在url上,我们需要使用request的data事件来获取post提交获得的数据。
  • postdata也是二进制数据,且使用toString()方法后编码任然可能不是正常编码,需要使用decodeURIComponent()方法进行解码。
  • 将表单提交的数据添加到数组中,使用模板引擎渲染到页面即可。

3.get提交的一些区别

首先,request.url会变成这样:

也就是多了后面的参数,那么if(url === '/addComment')就会失效,而request的data事件也不会触发。

所以如果是get提交,我们需要使用另外的处理方式。

  • 首先使用url核心模板即:var url = require('url');
  • 使用url核心模板的parse()方法来处理request.url。
//将路径解析为一个方便操作的对象,
//第二个参数为 true 表示直接将查询字符串转为一个对象(通过 query 属性来访问)
var parseObj = url.parse(req.url, true);
  • true这个参数很重要,否则parseObj.query会转化为字符串。
  • 此时parseObj对象就包含了url的相关信息,如parseObj.pathname表示当前请求连接(不带hash参数,本例中即为'/addComment')。而parseObj.query可以获取hash参数转化而成的对象,本例即:{name:xxx,messagexxx},且不用再使用decodeURIComponent()进行解码也不会乱码。

Node.js(day2)的更多相关文章

  1. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  2. 利用Node.js的Net模块实现一个命令行多人聊天室

    1.net模块基本API 要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用.NET模块API分为两大类:Server和Socket类.工厂方法. Server类 ...

  3. Node.js:进程、子进程与cluster多核处理模块

    1.process对象 process对象就是处理与进程相关信息的全局对象,不需要require引用,且是EventEmitter的实例. 获取进程信息 process对象提供了很多的API来获取当前 ...

  4. Node.js:理解stream

    Stream在node.js中是一个抽象的接口,基于EventEmitter,也是一种Buffer的高级封装,用来处理流数据.流模块便是提供各种API让我们可以很简单的使用Stream. 流分为四种类 ...

  5. Node.js:Buffer浅谈

    Javascript在客户端对于unicode编码的数据操作支持非常友好,但是对二进制数据的处理就不尽人意.Node.js为了能够处理二进制数据或非unicode编码的数据,便设计了Buffer类,该 ...

  6. node.js学习(二)--Node.js控制台(REPL)&&Node.js的基础和语法

    1.1.2 Node.js控制台(REPL) Node.js也有自己的虚拟的运行环境:REPL. 我们可以使用它来执行任何的Node.js或者javascript代码.还可以引入模块和使用文件系统. ...

  7. Node.js npm 详解

    一.npm简介 安装npm请阅读我之前的文章Hello Node中npm安装那一部分,不过只介绍了linux平台,如果是其它平台,有前辈写了更加详细的介绍. npm的全称:Node Package M ...

  8. Node.js入门(一)

    一.Node.js本质上是js的运行环境. 二.可以解析js代码(没有浏览器安全级的限制): 提供系统级的API:1.文件的读写 2.进程的管理 3.网络通信 三.可以关注的四个网站: 1.https ...

  9. Node.js学习笔记——Node.js开发Web后台服务

    一.简介 Node.js 是一个基于Google Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.Node.j ...

随机推荐

  1. selenium启动谷歌浏览器

    谷歌浏览器可以是任意版本. 根据谷歌浏览器的版本,下载响应的浏览器驱动(百度:谷歌驱动与谷歌浏览器版本映射表). 我用的是win7 ,下载win32就行了.下载解压之后可以放在python的安装目录下 ...

  2. 1、背景介绍及移动云MAS平台 --短信平台

    目的: 刚开发完成一套短信平台以及一个Web端短信发送系统,短信平台耗时两个周.短信发送系统耗时两个多月,开发使用的技术没什么高科技含量,在此主要是记录下很多情况的处理方案,希望能让大家提出改善方案和 ...

  3. 加密流量分析cisco

    思科ETA主页 https://www.cisco.com/c/en/us/solutions/enterprise-networks/enterprise-network-security/eta. ...

  4. Django ORM 常用字段和参数

    Django ORM 常用字段和参数 一:常用字段 AutoField int自增列,必须填入参数 primary_key=True.当model中如果没有自增列,则自动会创建一个列名为id的列. I ...

  5. 2,fiddler的基本设置

    1,首次打开的基本设置 2,过滤抓包的内容 1)较为常用的是: URL包含和hosts的设置 2)不常用的是 3,设置https 这是实在安装证书,当还是不能抓到https的包的时候,可以区安装彼得证 ...

  6. 如何将一个excel表格的数据匹配到另一个表中

    我们在操作excel表的时,有时需要将一个excel表中的数据匹配到另一个表中,那么就需要用到VLOOKUP函数,VLOOKUP函数是Excel中的一个纵向查找函数,VLOOKUP是按列查找,最终返回 ...

  7. 图解HTTP第三章

    HTTP 报文内的 HTTP信息 用于 HTTP 协议交互的信息被称为 HTTP 报文.HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的字符串文本. HTTP 报文大致可以分为两类:请 ...

  8. 第二周工作总结——NWNU李泓毅

    注:因作业未全部提交完毕,故评分细则和千帆图等评分事项推后 1.助教博客链接:https://www.cnblogs.com/lmcmha/ 2.本周点评作业: https://www.cnblogs ...

  9. LOJ-10104(割点+dfs)

    题目链接:传送门 思路: 求割点的同时求割点删除后所剩的不连通的点的对数,在遍历完成后回溯统计点的个数,具体操作见代码: 注意:结果是long long 类型. #include<iostrea ...

  10. 单个div充满屏幕的CSS方法

    1.通过定位实现 <style> *{ margin: 0; padding: 0; } div{ width:100%; height: 100%; background: yellow ...