从零开始,做一个NodeJS博客(二):实现首页-加载文章列表和详情
标签: NodeJS
0
这个伪系列的第二篇,不过和之前的几篇是同一天写的。三分钟热度貌似还没过。
1 静态资源代理
上一篇,我们是通过判断请求的路径来直接返回结果的。简单粗暴,缺点明显:如果url后面加杂了queryString,因为判断逻辑中没有处理,那么将直接返回404页面(其实也没有其他的页面)。
难道要一个一个加queryString的处理?有可行性,但麻烦。
再者,如果要添加新的html页面,那么也要在处理逻辑中依次添加?html页面还要引用js脚本,css样式,样式表中还要引用图片,字体。。。。难道就要这样无休止的添加下去?显然是不可能的。
借助于NodeJS提供的urlAPI,我们可以提取请求url中的pathName部分,也就是忽略了域名以及queryString的部分,然后将他交给另一个pathAPI。他负责将pathName解析为当前操作系统可读的路径。
至此,答案就很明显了:拿到路径后,利用fs模块读取,如果成功,则返回文件内容;否则直接404就好了。
这里我们只用到了
url和path模块的很小一部分功能,我也就只写这一点了。具体的定义及应用请参考NodeJS官方API文档。
url.prase( urlString )
接收一个url字符串,将它转为URL对象。
url.parse('http://user:pass@host.com:8080/path/to/file?query=string#hash');
它的返回值将会是一个 Object:
Url {
protocol: 'http:',
slashes: true,
auth: 'user:pass',
host: 'host.com:8080',
port: '8080',
hostname: 'host.com',
hash: '#hash',
search: '?query=string',
query: 'query=string',
pathname: '/path/to/file',
path: '/path/to/file?query=string',
href: 'http://user:pass@host.com:8080/path/to/file?query=string#hash'
}
看看这个结构图,应该更容易理解:
┌─────────────────────────────────────────────────────────────────────────────┐
│ href │
├──────────┬┬───────────┬─────────────────┬───────────────────────────┬───────┤
│ protocol ││ auth │ host │ path │ hash │
│ ││ ├──────────┬──────┼──────────┬────────────────┤ │
│ ││ │ hostname │ port │ pathname │ search │ │
│ ││ │ │ │ ├─┬──────────────┤ │
│ ││ │ │ │ │ │ query │ │
" http: // user:pass @ host.com : 8080 /p/a/t/h ? query=string #hash "
│ ││ │ │ │ │ │ │ │
└──────────┴┴───────────┴──────────┴──────┴──────────┴─┴──────────────┴───────┘
以上抄自:NodeJS官方API文档:URL
所以,要拿到相对路径,我们只需要取 Url.pathname 就可以了。
path.resolve( path[, ...] )
接收一组按顺序排好的路径,并把它组合为一个当前操作系统可识别的绝对路径。
path.resolve('.', '/archive/my-first-article')
如果在windows中,当前工作目录为 C:\Users\rocka,它将返回
'C:\Users\rocka\archive\my-first-article'
而在linux环境,/home/rocka目录中,它的返回值是
'/home/rocka/archive/my-first-article'
与当前操作系统直接关联。至于为何要跟当前工作目录关联,因为此时,它的第一个参数是 . ,不就是当前目录嘛。
path.join( path[, ...] )
接收一组路径字符串,然后将他们拼起来。可以用 .. 这样的相对路径。
返回值仍是字符串哦。
path.join('/foo', 'bar', 'baz/asdf', 'quux', '..')
将返回:
'/foo/bar/baz/asdf'
直接贴代码咯(代码片段,省略了require和listen等):
var root = path.resolve('.');
var server = http.createServer((request, response) => {
console.log(`[Rocka Node Server] ${request.method}: ${request.url}`);
// path name in url
var pathName = url.parse(request.url).pathname;
// file path based on operation system
var filePath = path.join(root, pathName);
console.log(`[Rocka Node Server] pathName: ${pathName}, filePath: ${filePath}`);
if (request.method === 'GET') {
// try to find and read local file
fs.stat(filePath, (err, stats) => {
// no error occured, read file
if (!err && stats.isFile()) {
response.writeHead(200, { 'Content-Type': 'text/html' });
fs.createReadStream(filePath).pipe(response);
// cannot find file, but received index request
} else if (!err && pathName == '/') {
response.writeHead(200, { 'Content-Type': 'text/html' });
fs.createReadStream('./page/index.html').pipe(response);
// file not found
} else if (!err && !stats.isFile()) {
response.writeHead(200, { 'Content-Type': 'text/html' });
fs.createReadStream('./page/404.html').pipe(response);
// error :(
} else if (err) {
response.writeHead(500);
response.end(err.toString());
}
});
}
});
嗯,看起来很工整。下面我们来试着跑一下。
哦,等会,根据刚才404页面和首页的位置,我的目录结构应该重构了,就像这样
- page
- 404.html
- index.html
- favicon.ico
- package.json
- Procfile
- server.js
之后打开监听的端口,看到首页了,favicon也正常加载,完成!
2 读取文章列表
上一篇我说过,暂时使用单独的文件来存放文章。所以文件名不仅要是文章的标题,还唯一标识着这篇文章对应的文件。
目录又要改了:
- archive
- my-first-article
- my-second-article
- page
- 404.html
- index.html
- script
- index.js
- favicon.ico
- package.json
- Procfile
- server.js
读取目录内所有文件,又要用到fs模块了。这次用的是 fs.readdir(path[, options], callback):
它读取指定目录内所有文件,并把两个参数 (err, files) 传递给回调函数。files 是一个包含目录内所有文件名的数组。
这样思路就很清晰了!我们读取 archive 下的所有文件,然后解析成json发出去就好。我说过要用Ajax的嘛。
(先别管怎么接收)
这个请求不同于文件请求,是一个“莫须有”的路径,需要我们来定义一下。我说了:
/api/index-article-list
这就是API路径。好了,现在把他写到服务器中去:
var server = http.createServer((request, response) => {
// parse the path
var pathName = url.parse(request.url).pathname;
var filePath = path.join(root, pathName);
if (request.method === 'GET') {
// this is a api request
if (pathName.indexOf('/api/') >= 0) {
switch (pathName) {
// api address
case '/api/index-article-list':
// read all archives
fs.readdir('./archive', (err, files) => {
if (err) {
console.log(err);
} else {
response.writeHead(200, { 'Content-Type': 'application/json' });
// parse the Array<String> to json string
response.end(JSON.stringify(files));
}
});
break;
default:
break;
}
} else {
// read local file
});
}
}
});
收到请求时,看看它是不是有定义的API请求。如果是的话,执行并返回,否则才去找文件。
服务端准备完成了,接下来是页面的动态加载:
贴一下我的 index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Rocka's Node Blog</title>
<script src="../script/index.js"></script>
</head>
<body>
<h1>Rocka's Node Blog</h1>
<hr>
<h3>Blog Archive</h3>
<ul id="index-article-list">
</ul>
<blockquote id="index-article-content">Article should be shown here.</blockquote>
</body>
</html>
接下来是 index.js
'use strict';
function loadArticleList() {
var ul = document.getElementById('index-article-list');
function success(response) {
var resData = JSON.parse(response);
resData.forEach((title) => {
var newLine = document.createElement('li');
newLine.innerHTML = `<a href="javascript:void(0);">${title}</a>`;
ul.appendChild(newLine);
});
}
function fail(code) {
var newLine = document.createElement('li');
newLine.innerText = `List Load Faild: Please Refresh Page And Try Again.`;
ul.appendChild(newLine);
}
var request = new XMLHttpRequest(); // New XMLHttpRequest Object
request.onreadystatechange = () => { // invoked when readyState changes
if (request.readyState === 4) { // request succeed
// response result:
if (request.status === 200) {
// succeed: update article
return success(request.response);
} else {
// failed: show error code
return fail(request.status);
}
}
}
// send request
request.open('GET', '/api/index-article-list');
request.send();
}
window.onload = () => {
console.log('Welcome to Rocka\'s Node Blog! ');
loadArticleList();
}
这里我用了js原生的 XMLHttpRequest ,并没有用jQuery提供的Ajax方法。
至于为什么,可能是之前一直用jQuery,有点审美疲劳了,想换个方法玩玩。
不过,一用上原生方法,我就发现,这是什么智障写法!!
一个破 Object ,还on这个on那个,自己还带那么多方法和属性,你以为你是谁啊,我凭什么要记这么多方法这么多属性!!!
果然jQuery大法好啊!!!!
Write less, do more. —— jQuery
3 读取文章详情
按照先前的定义,文章是存在单独的文件里的。里面只有一句简简单单的话:
| my-first-article | my-second-article |
|---|---|
| I'm Very Excited! | Stay young, Stay simple. |
于是这就很明显了!直接请求 /archive/${title} ,由于有这个文件的存在,内容就可以出来了!
这下连API都不用写了!
于是,愉快的在 index.js 中补刀一个函数(不要吐槽 XMLHttpRequest 的写法):
function loadArticleContent(articleTitle) {
var bq = document.getElementById('index-article-content');
function success(response) {
bq.innerText = response;
}
function fail(code) {
bq.innerText = 'Article Load Faild: Please Refresh Page And Try Again.';
bq.innerText += `Error Code: ${code}`;
}
var request = new XMLHttpRequest();
request.onreadystatechange = () => {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.response);
} else {
return fail(request.status);
}
}
}
request.open('GET', `/archive/${articleTitle}`);
request.send();
}
然后在加载标题时,把它的href指向这个函数就好了!
newLine.innerHTML = `<a href="javascript:loadArticleContent('${title}');">${title}</a>`;
保存,运行,刷新,提交,构建,完成!
愉快的一天就这么结束了233
仓库地址
GitHub仓库:BlogNode
主仓库,以后的代码都在这里更新。
HerokuApp:rocka-blog-node
上面GitHub仓库的实时构建结果。
从零开始,做一个NodeJS博客(二):实现首页-加载文章列表和详情的更多相关文章
- 从零开始,做一个NodeJS博客(四):服务器渲染页面与Pjax
标签: NodeJS 0 一个星期没更新了 = = 一直在忙着重构代码,以及解决重构后出现的各种bug 现在CSS也有一点了,是时候把遇到的各种坑盘点一下了 1 听歌排行 API 修复与重构 1.1 ...
- 从零开始,做一个NodeJS博客(零):整体规(chui)划(niu)
标签:NodeJS,Heroku 0 搭建一个个人独立博客,这是我好久之前就在计划的一件事了. 这个暑假,我学习了廖雪峰老师的NodeJS教程,又偶然在V2EX上发现了Heroku这个平台,可以免费在 ...
- 从零开始,做一个NodeJS博客(三):API实现-加载网易云音乐听歌排行
标签: NodeJS 0 研究了一天,翻遍了GitHub上各种网易云API库,也没有找到我想要的听歌排行API,可能这功能比较小众吧.但收获也不是没有,在 这里 明白了云音乐API加密的凶险,我等蒟蒻 ...
- 从零开始,做一个NodeJS博客(一):Heroku上的最简NodeJS服务器
标签:NodeJS,Heroku 0 这里是这个伪系列的第一篇,因为我也不知道自己能不能做完,然后到底能做成什么样子.总之,尽力而为吧,加油. 1 Heroku App 的构成 Heroku 所谓的 ...
- WordPress博客网站fonts.useso加载慢解决办法
WordPress博客网站fonts.useso加载慢解决办法 之前WordPress博客因为google字体库访问不了替换成360的useso,最近WordPress博客网站一直等待fonts.us ...
- JS window对象 返回下一个浏览的页面 forward()方法,加载 history 列表中的下一个 URL。
返回下一个浏览的页面 forward()方法,加载 history 列表中的下一个 URL. 如果倒退之后,再想回到倒退之前浏览的页面,则可以使用forward()方法,代码如下: window.hi ...
- 这几天有django和python做了一个多用户博客系统(可选择模板)
这几天有django和python做了一个多用户博客系统(可选择模板) 没完成,先分享下 断断续续2周时间吧,用django做了一个多用户博客系统,现在还没有做完,做分享下,以后等完善了再慢慢说 做的 ...
- 如何搭建一个独立博客——简明Github Pages与Hexo教程
摘要:这是一篇很详尽的独立博客搭建教程,里面介绍了域名注册.DNS设置.github和Hexo设置等过程,这是我写得最长的一篇教程.我想将我搭建独立博客的过程在一篇文章中尽可能详细地写出来,希望能给后 ...
- 用 JS 做一个数独游戏(二)
用 JS 做一个数独游戏(二) 在 上一篇博客 中,我们通过 Node 运行了我们的 JavaScript 代码,在控制台中打印出来生成好的数独终盘.为了让我们的数独游戏能有良好的体验,这篇博客将会为 ...
随机推荐
- 数据结构:JAVA_二叉数查找树基本实现(中)
数据结构:二叉数查找树基本实现(JAVA语言版) 1.写在前面 二叉查找树得以广泛应用的一个重要原因是它能保持键的有序性,因此我们可以把它作为实现有序符号表API中的众多方法的基础. 也就是说我们构建 ...
- Fort.js – 时尚、现代的表单填写进度提示效果
Fort.js 是一款用于时尚.现代的表单填写进度提示效果的 JavaScript 库,你需要做的就是添加表单,剩下的任务就交给 Fort.js 算法了,使用非常简单.提供了Default.Gradi ...
- java基础--相等
学习:http://www.cnblogs.com/dolphin0520/p/3780005.html#3163302 后发现居然有这么个东西,当然也不会注意什么自动拆箱和装箱,只知道用就行了.不过 ...
- 多iframe使用tab标签方式添加、删除、切换的处理实例
紧接着上一篇随笔iframe的内容增高或缩减时设置其iframe的高度的处理方案 如果采用iframe来切换显示内容的方式来展现办公Web.那么需要解决几个问题 1.tab标签需要和显示的iframe ...
- C# 根据自定义线程定时器 生成随机订单
这个源之于一个朋友问我的一个问题,他说他们的需求是在一天之内随机抽取数据生成订单,还不能让客户看出来. 随机生成的订单还分概率抽取不一定的状态值,那么根据我之前写的定时器线程执行器,我们设计需要一个定 ...
- SQL Server代理(8/12):使用SQL Server代理外部程序
SQL Server代理是所有实时数据库的核心.代理有很多不明显的用法,因此系统的知识,对于开发人员还是DBA都是有用的.这系列文章会通俗介绍它的很多用法. 在这个系列的上篇文章里,你学习如何使用SQ ...
- Bootstrap+angularjs+MVC3+分页技术+角色权限验证系统
1.Bootstrap使用教程 相关教程: http://www.bootcss.com/components.html 页面使用代码: <script src="@Url.Conte ...
- [Tool] 配置文件之Web.config
开发人员工具: 安装完vs后,(如2013:C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\Tools\Shortcuts\VS ...
- 使用Qt installer framework制作安装包
一.介绍 使用Qt库开发的应用程序,一般有两种发布方式:(1)静态编译发布.这种方式使得程序在编译的时候会将Qt核心库全部编译到一个可执行文件中.其优势是简单单一,所有的依赖库都集中在一起,其缺点也很 ...
- Win10怎么输入法切换
按 windows 键+空格键,或者CTRL+Shift,或者只按 Shift,或者CTRL+空格 试试 方法/步骤1在桌面上点击[控制面板],进入控制面板后使用分类显示控制面板内的选项.然后在语言下 ...