• node-blog记录


    require

    简单概括以下几点:

    • require 可加载 .js、.json 和 .node 后缀的文件
    • require 的过程是同步的,所以这样是错误的:
    1
    2
    3
    setTimeout(() => {
    module.exports = { a: 'hello' }
    }, 0)

    require 这个文件得到的是空对象 {}

    • require 目录的机制是:
      • 如果目录下有 package.json 并指定了 main 字段,则用之
      • 如果不存在 package.json,则依次尝试加载目录下的 index.js 和 index.node
    • require 过的文件会加载到缓存,所以多次 require 同一个文件(模块)不会重复加载
    • 判断是否是程序的入口文件有两种方式:
      • require.main === module(推荐)
      • module.parent === null

    npm

    1. npm i express --save/npm i express -S (安装 express,同时将 "express": "^4.14.0" 写入 dependencies )
    2. npm i express --save-dev/npm i express -D (安装 express,同时将 "express": "^4.14.0" 写入 devDependencies )
    3. npm i express --save --save-exact (安装 express,同时将 "express": "4.14.0" 写入 dependencies )

    第三种方式将固定版本号写入 dependencies,建议线上的 Node.js 应用都采取这种锁定版本号的方式,因为你不可能保证第三方模块下个小版本是没有验证 bug 的,即使是很流行的模块。

    req

    req 包含了请求来的相关信息,res 则用来返回该请求的响应,更多请查阅 express 官方文档。下面介绍几个常用的 req 的属性:

    • req.query: 解析后的 url 中的 querystring,如 ?name=haha,req.query 的值为 {name: 'haha'}
    • req.params: 解析 url 中的占位符,如 /:name,访问 /haha,req.params 的值为 {name: 'haha'}
    • req.body: 解析后请求体,需使用相关的模块,如 body-parser,请求体为 {"name": "haha"},则 req.body 为 {name: 'haha'}

    ejs

    ejs 有 3 种常用标签:

    1. <% code %>:运行 JavaScript 代码,不输出
    2. <%= code %>:显示转义后的 HTML内容
    3. <%- code %>:显示原始 HTML 内容

    注意:<%= code %><%- code %> 都可以是 JavaScript 表达式生成的字符串,当变量 code 为普通字符串时,两者没有区别。当 code 比如为 <h1>hello</h1> 这种字符串时,<%= code %> 会原样输出 <h1>hello</h1>,而 <%- code %> 则会显示 H1 大的 hello 字符串。

    更多 ejs 的标签请看 官方文档

    includes

    小提示:拆分模板组件通常有两个好处:

    1. 模板可复用,减少重复代码
    2. 主模板结构清晰

    注意:要用 <%- include('header') %> 而不是 <%= include('header') %>

    项目开始

    目录结构

    1. models: 存放操作数据库的文件
    2. public: 存放静态文件,如样式、图片等
    3. routes: 存放路由文件
    4. views: 存放模板文件
    5. index.js: 程序主文件
    6. package.json: 存储项目名、描述、作者、依赖等等信息

    相关依赖

    1. express: web 框架
    2. express-session: session 中间件
    3. connect-mongo: 将 session 存储于 mongodb,结合 express-session 使用
    4. connect-flash: 页面通知的中间件,基于 session 实现
    5. ejs: 模板
    6. express-formidable: 接收表单及文件上传的中间件
    7. config-lite: 读取配置文件
    8. marked: markdown 解析
    9. moment: 时间格式化
    10. mongolass: mongodb 驱动
    11. objectid-to-timestamp: 根据 ObjectId 生成时间戳
    12. sha1: sha1 加密,用于密码加密
    13. winston: 日志
    14. express-winston: express 的 winston 日志中间件

    配置文件

    config/default.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    module.exports = {
    port: 3000,
    session: {
    secret: 'myblog',
    key: 'myblog',
    maxAge: 2592000000
    },
    mongodb: 'mongodb://localhost:27017/myblog'
    }

    配置释义:

    1. port: 程序启动要监听的端口号
    2. session: express-session 的配置信息,后面介绍
    3. mongodb: mongodb 的地址,以 mongodb:// 协议开头,myblog 为 db 名

    config-lite 是一个轻量的读取配置文件的模块。config-lite 会根据环境变量(NODE_ENV)的不同加载 config 目录下不同的配置文件。如果不设置 NODE_ENV,则读取默认的 default 配置文件,如果设置了 NODE_ENV,则会合并指定的配置文件和 default 配置文件作为配置,config-lite 支持 .js、.json、.node、.yml、.yaml 后缀的文件。

    如果程序以 NODE_ENV=test node app 启动,则 config-lite 会依次降级查找 config/test.jsconfig/test.jsonconfig/test.nodeconfig/test.ymlconfig/test.yaml 并合并 default 配置; 如果程序以 NODE_ENV=production node app 启动,则 config-lite 会依次降级查找 config/production.jsconfig/production.jsonconfig/production.nodeconfig/production.ymlconfig/production.yaml 并合并 default 配置。

    config-lite 还支持冒泡查找配置,即从传入的路径开始,从该目录不断往上一级目录查找 config 目录,直到找到或者到达根目录为止。

    功能设计 v1.0

    路由设计

    功能及路由设计如下:

    1. 注册
      1. 注册页:GET /signup
      2. 注册(包含上传头像):POST /signup
    2. 登录
      1. 登录页:GET /signin
      2. 登录:POST /signin
    3. 登出:GET /signout
    4. 查看文章
      1. 主页:GET /posts
      2. 个人主页:GET /posts?author=xxx
      3. 查看一篇文章(包含留言):GET /posts/:postId
    5. 发表文章
      1. 发表文章页:GET /posts/create
      2. 发表文章:POST /posts/create
    6. 修改文章
      1. 修改文章页:GET /posts/:postId/edit
      2. 修改文章:POST /posts/:postId/edit
    7. 删除文章:GET /posts/:postId/remove
    8. 留言
      1. 创建留言:POST /comments
      2. 删除留言:GET /comments/:commentId/remove

    由于我们博客页面是后端渲染的,所以只通过简单的 <a>(GET)<form>(POST) 与后端进行交互,如果使用 jQuery 或者其他前端框架(如 Angular、Vue、React 等等)可通过 Ajax 与后端交互,则 api 的设计应尽量遵循 Restful 风格。

    更多阅读:

    1. ht 大专栏  node-blog记录tp://www.ruanyifeng.com/blog/2011/09/restful
    2. http://www.ruanyifeng.com/blog/2014/05/restful_api.html
    3. http://developer.51cto.com/art/200908/141825.htm
    4. http://blog.jobbole.com/41233/

    会话

    由于 HTTP 协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是会话(Session)。

    cookie 与 session 的区别

    1. cookie 存储在浏览器(有大小限制),session 存储在服务端(没有大小限制)
    2. 通常 session 的实现是基于 cookie 的,session id 存储于 cookie 中
    3. session 更安全,cookie 可以直接在浏览器查看甚至编辑

    更多 session 的资料,参考:https://www.zhihu.com/question/19786827

    我们通过引入 express-session 中间件实现对会话的支持:

    1
    app.use(session(options))

    session 中间件会在 req 上添加 session 对象,即 req.session 初始值为 {},当我们登录后设置 req.session.user = 用户信息,返回浏览器的头信息中会带上 set-cookie 将 session id 写到浏览器 cookie 中,那么该用户下次请求时,通过带上来的 cookie 中的 session id 我们就可以查找到该用户,并将用户信息保存到 req.session.user

    页面通知

    我们还需要这样一个功能:当我们操作成功时需要显示一个成功的通知,如登录成功跳转到主页时,需要显示一个 登陆成功 的通知;当我们操作失败时需要显示一个失败的通知,如注册时用户名被占用了,需要显示一个 用户名已占用 的通知。通知只显示一次,刷新后消失,我们可以通过 connect-flash 中间件实现这个功能。

    connect-flash 是基于 session 实现的,它的原理很简单:设置初始值 req.session.flash={},通过 req.flash(name, value) 设置这个对象下的字段和值,通过 req.flash(name) 获取这个对象下的值,同时删除这个字段,实现了只显示一次刷新后消失的功能。

    express-session、connect-mongo 和 connect-flash 的区别与联系

    1. express-session: 会话(session)支持中间件
    2. connect-mongo: 将 session 存储于 mongodb,需结合 express-session 使用,我们也可以将 session 存储于 redis,如 connect-redis
    3. connect-flash: 基于 session 实现的用于通知功能的中间件,需结合 express-session 使用

    权限控制

    不管是论坛还是博客网站,我们没有登录的话只能浏览,登陆后才能发帖或写文章,即使登录了你也不能修改或删除其他人的文章,这就是权限控制。我们也来给博客添加权限控制,如何实现页面的权限控制呢?我们可以把用户状态的检查封装成一个中间件,在每个需要权限控制的路由加载该中间件,即可实现页面的权限控制。

    可以看出:

    1. checkLogin: 当用户信息(req.session.user)不存在,即认为用户没有登录,则跳转到登录页,同时显示 未登录 的通知,用于需要用户登录才能操作的页面
    2. checkNotLogin: 当用户信息(req.session.user)存在,即认为用户已经登录,则跳转到之前的页面,同时显示 已登录 的通知,如已登录用户就禁止访问登录、注册页面

    注意:中间件的加载顺序很重要。如上面设置静态文件目录的中间件应该放到 routes(app) 之前加载,这样静态文件的请求就不会落到业务逻辑的路由里;flash 中间件应该放到 session 中间件之后加载,因为 flash 是基于 session 实现的。

    页面设计 v2.0

    app.locals 和 res.locals

    上面的 ejs 模板中我们用到了 blog、user、success、error 变量,我们将 blog 变量挂载到 app.locals 下,将 user、success、error 挂载到 res.locals 下。为什么要这么做呢?app.localsres.locals 是什么?它们有什么区别?

    express 中有两个对象可用于模板的渲染:app.localsres.locals

    可以看出:在调用 res.render 的时候,express 合并(merge)了 3 处的结果后传入要渲染的模板,优先级:res.render传入的对象> res.locals 对象 > app.locals 对象,所以 app.localsres.locals 几乎没有区别,都用来渲染模板,使用上的区别在于:app.locals 上通常挂载常量信息(如博客名、描述、作者这种不会变的信息),res.locals 上通常挂载变量信息,即每次请求可能的值都不一样(如请求者信息,res.locals.user = req.session.user)。

    连接数据库 v3.0

    https://github.com/mongolass/mongolass

    这里使用的是 mongolass

    注册 v4.0

    我们使用 express-formidable 处理表单的上传,表单普通字段挂载到 req.fields 上,表单上传后的文件挂载到 req.files 上,文件存储在 public/img 目录下。然后校验了参数,校验通过后将用户信息插入到 MongoDB 中,成功则跳转到主页并显示『注册成功』的通知,失败(如用户名被占用)则跳转回注册页面并显示『用户名已被占用』的通知。

    注意:我们使用 sha1 加密用户的密码,sha1 并不是一种十分安全的加密方式,实际开发中可以使用更安全的 bcryptscrypt 加密。 注意:注册失败时(参数校验失败或者存数据库时出错)删除已经上传到 public/img 目录下的头像。

    bug - fs.unlink Callback must be a function v4.0注册时删除头像

    登出与登录 v5.0

    文章 v6.0

    留言 v7.0

    404简单处理 v8.0

    忽略img和logs v9.0

    测试 v10.0

    修复fs.unlink以及控制台取消打印log v11.0

    腾讯公益404 v12.0

    1
    git checkout 版本号 # 切换学习时相应的版本

    启动

    1
    2
    3
    启动本地mongoDB数据库
    cd node-blog # 进入该项目
    node index.js # 启动

    部署

    https://github.com/nswbmw/N-blog/blob/master/book/4.15%20%E9%83%A8%E7%BD%B2.md#4154-%E9%83%A8%E7%BD%B2%E5%88%B0%E9%98%BF%E9%87%8C%E4%BA%91

    bug

    https://stackoverflow.com/questions/53583183/i-have-got-typeerror-err-invalid-callback-callback-must-be-a-function

    fs.unlink Callback must be a function v4.0注册时删除头像

  • 相关阅读:
    图形化代码阅读工具——Scitools Understand
    cocos studio UI 1.6.0.0 修改导出项目路径
    cocos2dx 中文路径编译错误记录
    利用特性区分查找方法,并通过反射调用方法
    WPF MVVM学习(二)
    wpf 控件模板、面板模板、数据模板
    blend 自定义控件
    wpf dataGrid样式
    blend 使用模板的几点说明
    WPF MVVM学习
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12401861.html
Copyright © 2020-2023  润新知