设为首页 收藏本站
查看: 924|回复: 0

[经验分享] 从零开始,做一个NodeJS博客(二):实现首页

[复制链接]

尚未签到

发表于 2017-2-22 12:18:32 | 显示全部楼层 |阅读模式
  标签: 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=&quot;en&quot;>
  

  
<head>
  <meta charset=&quot;UTF-8&quot;>
  <title>Rocka's Node Blog</title>
  <script src=&quot;../script/index.js&quot;></script>
  
</head>
  

  
<body>
  <h1>Rocka's Node Blog</h1>
  <hr>
  <h3>Blog Archive</h3>

  <ul>  </ul>

  <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=&quot;javascript:void(0);&quot;>${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=&quot;javascript:loadArticleContent('${title}');&quot;>${title}</a>`;  保存,运行,刷新,提交,构建,完成!
  愉快的一天就这么结束了233

仓库地址

GitHub仓库:BlogNode

  主仓库,以后的代码都在这里更新。


HerokuApp:rocka-blog-node

  上面GitHub仓库的实时构建结果。


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-345756-1-1.html 上篇帖子: Nodejs+express 实战,实现系统监控功能 下篇帖子: nodejs实现Websocket的数据接收发送
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表