NodeJS七天课程学习笔记_第8天 Blog综合案例
Blog 综合案例 (包含注册、登录、修改密码、注销、发布、分页列表、评论、个人中心、上传头像等)
课程内容概要:
1. 介绍art-template中的 子模板 与 模板继承
2. 介绍表单同步提交与异步提交的区别
3. 介绍了可视化管理工具MongoBooster
4. 介绍了blueimp/javascript-MD5的配置和使用
5. 介绍express-session的配置与使用
6. 注册、登录 与 退出 功能的实现
7. 介绍 Express 中间件 原理
8. express中的 三大类 中间件
9. express中的 全局统一错误处理
注意: Node中有许多第3方模板引擎,不只有art-template,还有:
ejs (e代表effective高效之意,从json中生成html的一种魔法) 、
jade(因版权问题,后改名pug哈巴狗)、
handlebars(号称logic-less templateheima的manmanmai项目用到)、
nunjucks(是Mozilla开发的一个纯JavaScript编写的模板引擎)
首先讲art-template中的子模板(include) 以及 模板继承(extend)语法
官方文档地址 aui.github.io/art-template/zh-cn/docs/syntax.html
第1步, 新建下面4个文件 作为将来 被包含的子模板
公共的顶部public_top.html、
公共的底部public_footer.html、
公共的左侧边栏public_left.html、
公共的右广告栏public_right.html
第2步,新增 node_51_base_layout.html 作为将来被各个实际页面继承(extend)的母板
母板里面有个完整的骨架,
母板里包含(include)了公共的顶部、底部、左侧边栏、右广告栏
母板里head引入所有页面要用到的公共的css(如bootstrap)、js(如jquery)
同时,母板里,
通过block语法,在head标签中 预留了 实际页面真正要用到的 title、css、 js空间
通过block语法,在body标签中 预留了 实际页面真正要用到的html 空间
node_51_base_layout.html母板完整代码如下:
{{ block 'block_1_title'}}
beyond心中の动漫神作
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ include './public_top.html' }}
{{ include './public_left.html' }}
{{ block 'block_3_body_content'}}
这是母板默认的正文
{{ /block }}
{{ include './public_right.html' }}
{{ include './public_footer.html' }}
{{ block 'block_4_body_js'}}
{{ /block }}
第3步,现在就可以新建首页node_51_index.html了 ,
该首页 继承(extend) 自 base_layout.html母板
node_51_index.html完整代码如下:
{{ extend './node_51_base_layout.html' }}
{{ block 'block_1_title'}}
自己的标题_首页
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ block 'block_3_body_content'}}
自己的正文
{{ /block }}
{{ block 'block_4_body_js'}}
{{ /block }}
入口文件node_51_index.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 导入框架
var express = require('express')
var path = require('path')
// 创建服务器对象
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
if (error) {
return NSLog('启动失败: ' + error)
}
NSLog('服务启动成功')
})
// -----------------------------------
// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public'
// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录
var staticFilePath = './public/'
// var staticFilePath = 'public'
var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// 再开一个静态资源目录
appServer.use('/uploads/',express.static(path.join(__dirname,'uploads')))
// -----------------------------------
// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')
// -----------------------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))
// 设置解析 application/json
appServer.use(bodyParser.json())
// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
// 1.1 创建服务
// 1.2 做一些服务相关的配置,比如:
// 1.2.1 静态资源配置
// 1.2.2 模板引擎配置
// 1.2.3 body-parse 解析表单
// 1.2.4 挂载自定义路由
// 1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_51_router')
appServer.use(beyondRouter)
路由文件node_51_router.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// -----------------------------------
// 时间格式化
// var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
response.render('index/node_51_index.html')
})
// 3.在模块文件最后,导出router
module.exports = router
通过node_51_index.js入口 加载 node_51_router.js路由之后
启动服务器,渲染效果如下:
Blog项目开始
第1步. 项目初始化
首先npm init -y ,生成package.json
然后git init,然后手动新建.gitignore文件
第2步. npm 安装 mongoose和express和art-template和express-art-template
第3步. 项目目录
public目录下有img和css和js和lib目录
views目录下放着html模板, 分成了登录注册模块、文章模块
第4步. 路由设计
请求路由 | 方法 | GET参数 | POST参数 | 是否需要登录权限(没用到) | 备注 |
/ | GET | 首页index.html即文章列表 | |||
/register | GET | 注册页面register.html | |||
/register | POST | email,password,username | 处理注册的POST请求 | ||
/login | GET | 登录页面login.html | |||
/login | POST | email,password | 处理登录POST请求 |
首先处理的路由是 / , 渲染首页node_52_index.html
由于数据库内暂时还没有数据,所以首页只用几个假的数据先填充,以保证样式正常
首页的渲染效果如下:
首页node_52_index.html代码(暂未使用模板引擎渲染)如下:
{{ extend './node_52_base_layout.html' }}
{{ block 'block_1_title'}}
未闻花名_多用户博客系统_首页
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ block 'block_3_body_content'}}
{{ /block }}
{{ block 'block_4_body_js'}}
{{ /block }}
注意: 首页node_52_index.html 是 继承(extend)自 母板 node_52_base_layout.html
母板node_52_base_layout.html代码如下:
{{ block 'block_1_title'}}
beyond心中の动漫神作
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ include './node_52_public_top.html' }}
{{ block 'block_3_body_content'}}
这是母板默认的正文
{{ /block }}
{{ include './node_52_public_footer.html' }}
{{ block 'block_4_body_js'}}
{{ /block }}
注意:
母板node_52_base_layout.html中用到的子模板node_52_public_top.html 以及
子模板node_52_public_footer.html代码分别如下:
子模板node_52_public_top.html代码如下: (一会儿写了登录注册后,再完善)
子模板node_52_public_footer.html代码如下:
目前为止,首页,效果如下:
接下来,点击首页->右上角按钮->弹出下拉菜单->单击注册按钮
注册页面效果如下:
开始渲染node_52_register.html注册页面: (包括使用ajax发送异步请求(POST注册)):
代码如下:
{{ extend './node_52_base_layout.html' }}
{{ block 'block_1_title'}}
未闻花名_多用户博客系统_注册
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ block 'block_3_body_content'}}
{{ /block }}
{{ block 'block_4_body_js'}}
{{ /block }}
接下来编写服务端的代码, 以便处理ajax提交post过来的注册请求
这儿我们使用mongodb + mongoose中间件
第1步, 打开命令行,执行mongod 启动mongodb数据库服务
第2步,新建node_52_userdao.js, 创建User数据模型Schema
(注意: 后面我们还会创建articledao.js用来操作文章入库的CRUD)
(注意: 后面我们还会创建commentdao.js用来操作评论入库的CRUD)
node_52_userdao.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
// ----------------初始化-------------------
var mongoose = require('mongoose')
// 连接数据库 db2,默认端口是: 27017
mongoose.connect('mongodb://localhost/db2')
var Schema = mongoose.Schema
// 设计用户表结构
var userSchema = new Schema({
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
pubTime: {
type: String,
required: true,
/*
type: Date,
default: Date.now
默认值: 只写一个函数名,不加括号就不会立即调用,
因为一旦加括号,就会立即调用,会立刻执行
*/
default: ''
},
usersex: {
type: Number,
required: false,
default: 0
},
userage: {
type: Number,
// enum: [8,9,10,11,12,13,14,15,16],
default: 13
},
userimg: {
// 用户头像url
type: String,
required: false,
default: 'beyond.jpg'
},
userdescription: {
type: String,
required: false,
default: 'vwhm.net'
},
userstatus: {
// 0无限制,1限制评论,2限制发布,3限制登录
type: Number,
enum: [0,1,2,3],
default: 0
}
})
// 核心代码: 直接导出 模型构造函数
// 参数1: User 首字母必须大写,且必须是单数;
// 这样就能自动生成users表(集合)
module.exports = mongoose.model('User',userSchema)
第3步,安装和配置body-parser中间件,自动化处理表单请求
第4步,保存入库,并将操作结果的json数据 回写浏览器
这个保存之前,要先查询 email 或者 username是否已经存在,
使用到了mongoose官方文档 中的 or 语法,mongoosejs.com/docs/api.html#query_Query-or
也可以参照mongodb官方文档 中的 or 条件语法
在node_52_router.js中,处理post过来的register请求时,
先判断数据库中是否已经存在email和username
如果查询错误,那么给浏览器报错500,Server Error如果使用or查询,为了少写一个接口,那么给浏览器报错err = 3,email已注册或username已存在
如果查询结果为null,那么执行注册save操作,
如果save结果出错,那么给浏览器报错500
如果save成功,那么给浏览器返回{"err":0,"msg":"注册成功"}
浏览器就会收到异步请求返回的结果,进行客户端跳转了
在这期间, 插句题外话,推荐了一个客户端软件 MongoBooster 对MongoDB数据库进行可视化管理
安装后, 通过url localhost:27017 进行连接mongodb数据库
然后就可以选择db2数据库,执行查询语句了...
在这期间,还插了一句题外话,使用md5 中间件
在github上,通过node + md5关键字, 搜索到了blueimp/JavaScript-MD5
客户端直接引入,然后使用
<script src="public/js/md5.min.js">script>
就可以使用了
var hash = md5("password");
服务端先 npm install blueimp-md5 ,然后使用:
var hash = md5("password");
最后,处理注册请求的node_52_router.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
response.render('index/node_52_index.html')
})
// ----------------注册页面-------------------
router.get('/register',function (request,response) {
response.render('index/node_52_register.html')
})
// ----------------进行注册-------------------
router.post('/register',function (request,response) {
/*
先判断数据库中是否已经存在email和username
如果查询错误,报错500,Server Error
如果email已注册,报错err = 1
如果username已存在,报错err = 2
如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
*/
// 直接获取body-parser中post过来的表单
var bodyObj = request.body
// 使用blueimp-md5进行加密 (可加两次)
bodyObj.password = md5(md5(bodyObj.password))
// 手动添加当前时间
bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')
UserDaoFunction.findOne({
$or: [
{
email: bodyObj.email
},
{
username: bodyObj.username
}
]
})
.then(function (data) {
// then方法的参数1是: resolveCallback
NSLog('data: ' + data)
if (data === null) {
// 来到这儿说明 可以进行真正的注册
// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
var promise_2 = new UserDaoFunction(bodyObj).save()
return promise_2
}
// 来到这儿,说明有data 则表示 已存在了
response.status(200).json({
err: 3,
msg: 'email已注册或username已存在'
})
},function (error) {
// then方法的参数2是: rejectCallback
// 如果查询失败,则返回500错误
NSLog('error: ' + error)
// express 内部 提供了一个json方法,自动把对象转成json
response.status(500).json({
err: 500,
msg: error
})
})
.then(function (data) {
// then方法的参数1是: promise_2的resolveCallback
// 保存成功
response.status(200).json({
err: 0,
msg: '注册成功'
})
},function (error) {
// then方法的参数2是: promise_2的rejectCallback
// 保存失败
response.status(500).json({
err: 500,
msg: error
})
})
})
// 3.在模块文件最后,导出router
module.exports = router
注册效果运行如下:
打开MongoBooster,使用command + R刷新一下, 查看数据库如下:
再次强调了一下, 服务端重定向,对于浏览器的异步请求无效!!! 记住就ok
表单具有默认的提交行为,默认是同步的,表单同步提交,浏览器会锁死(转圈儿)等待服务端的响应结果。
表单的同步提交之后,无论服务端响应的是什么,都会直接把响应的结果覆盖掉当前页面。
因此,以前的开发时,还要实现数据回显,即在重新渲染页面时,把前面提交过来的数据 填充到表单元素中
注意: 同步提交返回的结果 是直接覆盖掉 当前页面的内容后来有人想到了异步提交的办法,来解决这个问题。
如今github仍然使用的是表单同步提交, 就是因为这样由服务端统一渲染,比较安全,虽然服务器压力大一些
补充一下,github 是一个 ruby on rails项目
由于express默认是不支持cookie和session的,
因此, 下面 使用express-session中间件,来保存用户登录的状态
express-session 官方文档: npmjs.com/package/express-session
第1步, 安装
npm install express-session --save
第2步, 配置 (一定要在appServer.use(router)之前)
// ---------express-session中间件配置------------------
// express-session 步骤2
appServer.use(session({
// 密钥,为了安全起见
secret: 'vwhm.net',
//
resave: false,
// true表示 尚未使用 就进行初始化一个sessionID
saveUninitialized: true
}))
第3步, 使用request.session.user 即可 设置或读取 session
(注意:此状态下的session 不是持久化的, 只是内存里,重启服务器就没有了)
我们在UserDao保存注册用户的时候,就将save()方法返回的user对象,存入session内
request.session.user = userFromDB
第4步, 在渲染node_52_index.html时, 将session中的user对象渲染过去
如果session有user,那么 显示 用户名
如果session没有user,那么 显示 登录和注册
第5步, 注销的话,只要 置null 并 delete request.session.user即可
效果如下:
node_52_index.js代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// --------导入框架---------------------------
var express = require('express')
var path = require('path')
// express-session 步骤1
var session = require('express-session')
// --------创建并启动服务器--------------------------
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
if (error) {
return NSLog('启动失败: ' + error)
}
NSLog('服务启动成功')
})
// --------静态资源配置---------------------------
// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public'
// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录(强烈推荐转成绝对路径)
// var staticFilePath = './public/'
// var staticFilePath = 'public'
var staticFilePath = path.join(__dirname,'public')
var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// 再开一个静态资源目录
appServer.use('/uploads/',express.static(path.join(__dirname,'uploads')))
// ---------art-template模板引擎配置-------------------
// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')
// ---------body-parser中间件配置---------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))
// 设置解析 application/json
appServer.use(bodyParser.json())
// ---------express-session中间件配置------------------
// express-session 步骤2
// 该中间件 会为request增加一个session的成员,默认类型为对象
// 本例中,session是在内存中保存的,一旦服务器重启就不在了
// 在实际生产环境下,需要对session进行持久化
appServer.use(session({
// 自定义密钥,为了安全起见
secret: 'vwhm.net',
//
resave: false,
// true表示 无论用不用session 都进行初始化一个sessionID
saveUninitialized: true
}))
// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
// 1.1 创建服务
// 1.2 做一些服务相关的配置,比如:
// 1.2.1 静态资源配置
// 1.2.2 模板引擎配置
// 1.2.3 body-parse 解析表单
// 1.2.4 挂载自定义路由
// 1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_52_router')
appServer.use(beyondRouter)
node_52_router.js代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
response.render('index/node_52_index.html',{
/*
在渲染index.html时, 将session中的user对象渲染过去
如果session有user,那么 显示 用户名
如果session没有user,那么 显示 登录和注册
*/
user: request.session.user
})
})
// ----------------注册页面-------------------
router.get('/register',function (request,response) {
response.render('index/node_52_register.html')
})
// ----------------进行注册-------------------
router.post('/register',function (request,response) {
/*
先判断数据库中是否已经存在email和username
如果查询错误,报错500,Server Error
如果email已注册,报错err = 1
如果username已存在,报错err = 2
如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
*/
// 直接获取body-parser中post过来的表单
var bodyObj = request.body
// 使用blueimp-md5进行加密 (可加两次)
bodyObj.password = md5(md5(bodyObj.password))
// 手动添加当前时间
bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')
UserDaoFunction.findOne({
$or: [
{
email: bodyObj.email
},
{
username: bodyObj.username
}
]
})
.then(function (data) {
// then方法的参数1是: resolveCallback
NSLog('data: ' + data)
if (data === null) {
// 来到这儿说明 可以进行真正的注册
// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
var promise_2 = new UserDaoFunction(bodyObj).save()
return promise_2
}
// 来到这儿,说明有data 则表示 已存在了
response.status(200).json({
err: 3,
msg: 'email已注册或username已存在'
})
},function (error) {
// then方法的参数2是: rejectCallback
// 如果查询失败,则返回500错误
NSLog('error: ' + error)
// express 内部 提供了一个json方法,自动把对象转成json
response.status(500).json({
err: 500,
msg: error
})
})
.then(function (userFromDB) {
// then方法的参数1是: promise_2的resolveCallback
// 保存成功的话, 记录到session中
request.session.user = userFromDB
response.status(200).json({
err: 0,
msg: '注册成功'
})
},function (error) {
// then方法的参数2是: promise_2的rejectCallback
// 保存失败
response.status(500).json({
err: 500,
msg: error
})
})
})
// ----------------注销请求-------------------
router.get('/logout',function (request,response) {
// 清除session 并 重定向到首页
request.session.user = null
delete request.session.user
response.redirect('/')
})
// 3.在模块文件最后,导出router
module.exports = router
最后再把登录功能实现一下 (后面的上传头像、发布、评论、找回密码等等等以后再写)
node_52_login.html代码如下:
{{ extend './node_52_base_layout.html' }}
{{ block 'block_1_title'}}
未闻花名_多用户博客系统_注册
{{ /block }}
{{ block 'block_2_head_css_js'}}
{{ /block }}
{{ block 'block_3_body_content'}}
{{ /block }}
{{ block 'block_4_body_js'}}
{{ /block }}
包含了登录处理请求的node_52_router.js完整代码如下:
function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};
/*
自定义路由模块的职责是:
专门处理所有的路由
根据不同的请求方式和路径,采取相应的处理方法
*/
// express 专门提供了路由的处理方法
var express = require('express')
// blueimp-md5对password加密再存入数据库
var md5 = require('./md5')
// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------
// 1.使用express专门提供的路由器处理路由
var router = express.Router()
// ----------------引入dao模块-------------------
// 先对dao初始化
var UserDaoFunction = require('./node_52_userdao')
// 时间格式化
var BeyondDateFormatFunction = require('./BeyondDateFormat')
// ----------------首页-------------------
router.get('/',function (request,response) {
response.render('index/node_52_index.html',{
/*
在渲染index.html时, 将session中的user对象渲染过去
如果session有user,那么 显示 用户名
如果session没有user,那么 显示 登录和注册
*/
user: request.session.user
})
})
// ----------------注册页面-------------------
router.get('/register',function (request,response) {
response.render('index/node_52_register.html')
})
// ----------------进行注册-------------------
router.post('/register',function (request,response) {
/*
先判断数据库中是否已经存在email和username
如果查询错误,报错500,Server Error
如果email已注册,报错err = 1
如果username已存在,报错err = 2
如果使用or查询,为了少写一个接口,报错err = 3,email已注册或username已存在
*/
// 直接获取body-parser中post过来的表单
var bodyObj = request.body
// 使用blueimp-md5进行加密 (可加两次)
bodyObj.password = md5(md5(bodyObj.password))
// 手动添加当前时间
bodyObj.pubTime = BeyondDateFormatFunction(new Date(),'yyyy-MM-dd hh:mm:ss')
UserDaoFunction.findOne({
$or: [
{
email: bodyObj.email
},
{
username: bodyObj.username
}
]
})
.then(function (data) {
// then方法的参数1是: resolveCallback
NSLog('data: ' + data)
if (data === null) {
// 来到这儿说明 可以进行真正的注册
// new 一个对象, 然后执行save方法并生成一个promise,为了避免callback hell 我们把它return, 目的是使用链式的then方法
var promise_2 = new UserDaoFunction(bodyObj).save()
return promise_2
}
// 来到这儿,说明有data 则表示 已存在了
response.status(200).json({
err: 3,
msg: 'email已注册或username已存在'
})
},function (error) {
// then方法的参数2是: rejectCallback
// 如果查询失败,则返回500错误
NSLog('error: ' + error)
// express 内部 提供了一个json方法,自动把对象转成json
response.status(500).json({
err: 500,
msg: error
})
})
.then(function (userFromDB) {
// then方法的参数1是: promise_2的resolveCallback
// 保存成功的话, 记录到session中
request.session.user = userFromDB
response.status(200).json({
err: 0,
msg: '注册成功'
})
},function (error) {
// then方法的参数2是: promise_2的rejectCallback
// 保存失败
response.status(500).json({
err: 500,
msg: error
})
})
})
// ----------------注销请求-------------------
router.get('/logout',function (request,response) {
// 清除session 并 重定向到首页
request.session.user = null
delete request.session.user
response.redirect('/')
})
// ----------------登录界面-------------------
router.get('/login',function (request,response) {
response.render('index/node_52_login.html')
})
// ----------------进行登录-------------------
router.post('/login',function (request,response) {
// 1. 获取表单,对密码二次加密
// 直接获取body-parser中post过来的表单
var bodyObj = request.body
// 使用blueimp-md5进行加密 (可加两次)
bodyObj.password = md5(md5(bodyObj.password))
// 2. 使用UserDao查询
UserDaoFunction.findOne({
email: bodyObj.email,
password: bodyObj.password
})
.then(function (userFromDB) {
// 如果 userFromDB 为 null,表示 登录失败
if (userFromDB === null) {
return response.status(200).json({
err: 1,
msg: '邮箱或密码错误'
})
}
// 来到这儿说明登录成功,记录session
request.session.user = userFromDB
response.status(200).json({
err: 0,
msg: '登录成功'
})
},function (error) {
// then方法的参数2是: rejectCallback
// 如果查询失败,则返回500错误
NSLog('error: ' + error)
// express 内部 提供了一个json方法,自动把对象转成json
response.status(500).json({
err: 500,
msg: error
})
})
})
// 3.在模块文件最后,导出router
module.exports = router
最终效果如下: ((后面的上传头像、使用xheditor发布、评论、找回密码等等等以后再写))
NodeJS七天课程学习笔记_第8天 中间件_全局错误处理
推荐了chrome插件: EditThisCookie
推荐了模拟各种post和get等请求的工具: PostMan
一张图说明中间件的原理
中间件: 实质上是一种包装方法
中间件分为几种:
1. 不关心任何请求路径的中间件,如use方法
appServer.use( function(request,response,next ){
NSLog('请求被拦截下来了')
// 请求又被放行了
next()
}
)
意思是任何请求,都会进入这个中间件
任何请求都会被这个use方法拦截下来, 后面的中间件就无法再获取到该请求了
除非在use方法最后一行, 调用next() ,放行该请求
2. 只关心 以/public开头的中间件, 如 /public/img/beyond.jpg或者/public/js/jquery.js
像这样的只有是以/public开头的请求,才会被拦截下来(不关心是post还是get方式)
appServer.use('/public', function(request,response,next ){
NSLog('请求被拦截下来了')
// 请求又被放行了
next()
}
)
3. 严格匹配 请求方式 与 请求路径的中间件, 如get 和 post等
像这样的只有是/login的Get请求,才会被拦截下来
appServer.get('/login', function(request,response,next ){
NSLog('请求被拦截下来了')
// 请求一般被处理了,不会再放行了, 要放行也可以...
next()
}
)
4. 错误处理中间件, 集中处理错误, use的参数只有1个函数, 该匿名函数 必须是四个参数
appServer.use(function(error,request,response,next ){
console.error(error.stack)
// 请求一般被处理了,不会再放行了, 要放行也可以...
next()
}
)
node_53.js完整代码如下:
function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}
// 演示express中的三种中间件
var express = require('express')
var appServer = express()
/*
第1种,不关心任何请求路径的中间件:
use有两个参数:
参数1: 函数(带3个参数)
任何请求,都会被拦截下来
除非 手动调用next()才会将请求放行
*/
appServer.use(function (request,response,next) {
NSLog('第1种_1:' + request.url,false)
next()
})
appServer.use(function (request,response,next) {
NSLog('第1种_2:' + request.url,false)
next()
})
/*
第2种,不关心请求方式, 只关心请求路径是以xxx开头的中间件: use有两个参数:
参数1: 以xxx开头的路径
参数2: 函数(带3个参数)
任何以xxx开头的请求,都会被拦截下来
除非 手动调用next()才会将请求放行
*/
appServer.use('/public',function (request,response,next) {
// 如果此时浏览器输入: localhost/public/img/beyond.jpg
// 注意这时的url打印出来 就不再包含/public前缀了
// 而是只有后半部分: /img/beyond.jpg
NSLog('第2种_以public开头:' + request.url,false)
})
/*
第3种,路由级别的中间件
既严格匹配请求方式(get/post/put/delete等), 又严格匹配请求路径的中间件
use有两个参数:
参数1: 严格匹配的路径
参数2: 函数(带3个参数)
任何精准匹配路径 并且 符合请求方式 的请求,都会被拦截下来
除非 手动调用next()才会将请求放行
*/
appServer.get('/login',function (request,response,next) {
NSLog('第3种_GET_Login:' + request.url,false)
})
appServer.post('/login',function (request,response,next) {
NSLog('第3种_POST_Login:' + request.url,false)
})
// 最后,如果,没有能匹配的中间件,则Express 默认会提示Cannot GET /xxx
/*
第4种, 错误处理中间件
use参数只有1个 匿名函数
该匿名函数的参数 必须是 4个
*/
appServer.use(function (error,request,response,next) {
// 理论上是这样设置的, 但为啥不生效???
NSLog('第4种_error:' + request.url,false)
console.error(error.stack)
response.status(500),send('404 Not Found')
})
// 监听端口,启动服务
// 访问地址:localhost:5267
appServer.listen(5267,function (error) {
if (error) {
return NSLog('启动失败: ' + error,false)
}
NSLog("服务启动成功",false)
})
效果如下: (第4种,全局错误处理,不知道为啥 不生效???!!!)
附录: express中关于中间件的 大概介绍
Express is a routing and middleware web framework that has minimal functionality of its own: An Express application is essentially a series of middleware function calls.
Middleware functions are functions that have access to the request object (req
), the response object (res
), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next
.
Middleware functions can perform the following tasks:
If the current middleware function does not end the request-response cycle, it must call next()
to pass control to the next middleware function. Otherwise, the request will be left hanging.
An Express application can use the following types of middleware:
You can load application-level and router-level middleware with an optional mount path.
You can also load a series of middleware functions together, which creates a sub-stack of the middleware system at a mount point.
Error-handling middleware always takes four arguments. You must provide four arguments to identify it as an error-handling middleware function. Even if you don’t need to use the next
object, you must specify it to maintain the signature. Otherwise, the next
object will be interpreted as regular middleware and will fail to handle errors.
Define error-handling middleware functions in the same way as other middleware functions, except with four arguments instead of three, specifically with the signature (
err, req, res, next
)
):
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
内置中间件:
http://expressjs.com/en/guide/using-middleware.html
Middleware module | Description | Replaces built-in function (Express 3) |
---|---|---|
body-parser | Parse HTTP request body. See also: body, co-body, and raw-body. | express.bodyParser |
compression | Compress HTTP responses. | express.compress |
connect-rid | Generate unique request ID. | NA |
cookie-parser | Parse cookie header and populate req.cookies . See also cookies and keygrip. |
express.cookieParser |
cookie-session | Establish cookie-based sessions. | express.cookieSession |
cors | Enable cross-origin resource sharing (CORS) with various options. | NA |
csurf | Protect from CSRF exploits. | express.csrf |
errorhandler | Development error-handling/debugging. | express.errorHandler |
method-override | Override HTTP methods using header. | express.methodOverride |
morgan | HTTP request logger. | express.logger |
multer | Handle multi-part form data. | express.bodyParser |
response-time | Record HTTP response time. | express.responseTime |
serve-favicon | Serve a favicon. | express.favicon |
serve-index | Serve directory listing for a given path. | express.directory |
serve-static | Serve static files. | express.static |
session | Establish server-based sessions (development only). | express.session |
timeout | Set a timeout period for HTTP request processing. | express.timeout |
vhost | Create virtual domains. | express.vhost |
未完待续,下一章节,つづく