《Nodejs实战》笔记五:用Express实现留言版功能
3. Express
Express是在connect基础上搭建的, 先安装express的脚手架
npm install -g express-generator
使用ejs模板引擎
express -e express-project
3.1 留言版项目规划
功能需求
- 用户可以注册,登录,退出
- 用户可以发消息
- 访问者可以分页浏览条目
- 有一个简单的REST API
3.2 渲染视图
Express几乎支持所有的模板引擎,本项目使用ejs
Express有两种渲染视图的方法:
- app.render() 在程序层使用
- res.render() 在请求或者响应层使用
本项目只会使用res.render()
3.2.1 了解渲染机制
主要介绍三方面
- 调整视图的查找
- 配置默认的模板引擎
- 启用视图缓存,减少文件I/O
首先是设置 views, 给程序指明渲染视图的路径
app.set('views', __dirname + '/views')
ps: __dirname
表示当前文件的目录
然后指定渲染引擎
app.set('view engine', 'ejs');
视图缓存
视图缓存view cache
是默认开启的,
如果不开视图缓存, 每次渲染
rers.render('user', {name: 'zmj'})
都会从硬盘中读取html
如果开启的,读取了一次html后,会缓存好,下次再读取的时候直接读取缓存,而不用再去磁盘上读了
3.2.2 将数据传给视图
- res.render()
- res.locals
- app.locals
3.3 express路由入门
这一节要做留言版的两个功能
- 用特定路由的中间件校验用户提交的内容
- 实现特定路由的校验
为了实现校验,给这个程序加上消息提交的功能(用户不提交消息,你咋校验=。=)
3.3.1 消息提交功能
消息提交功能要完成以下几点
- 创建消息模型
- 添加与消息相关的路由
- 创建消息表单
- 添加业务逻辑
3.3.2 创建消息模型
表示没有接触过redis,=。=,硬着头皮来吧。
这里使用到了redis,安装指南
去github上下win10版本 https://github.com/MicrosoftA...
下载msi的
省事,会自动帮你配好服务和环境
在cmd中测试一下
redis-cli.exe -h 127.0.0.1 -p 6379 设置键值对 set myKey abc 取出键值对 get myKey
这里我们安装好了redis服务器后,再去项目里安装redis模块
npm install --save redis
redis的文档 readme
中文命令手册命令
官网的api api
redis支持很多数据结构,基本常见的都支持这里使用到了列表
创建models/entry.js
const redis = require('redis') const db = redis.createClient() class Entry { static getRange(from, to, cb) { db.lrange('entries', from, to, (err, items) => { if (err) return cb(err) let entries = [] items.forEach((item) => { entries.push(JSON.parse(item)) }) cb(null, entries) }) } constructor(obj) { // obj = {a: 1, b: 2, c: 3} // this[a] = 1, this[b] = 2, this[c] = 3 for (let key in obj) { this[key] = obj[key] } } save(cb) { // 将消息转化为字符串,保存到Redis列表中 const entryJSON = JSON.stringify(this) db.lpush('entries', entryJSON, (err) => { if (err) return cb(err) cb() }) } } module.exports = Entry
lpush是redis像列表中插入数据的方法,第一个参数是键名,第二个是值,第三个是回调
lrange是查询数据的方法,第一个参数是键名,第二和第三个是起始位置和结束位置,第四个是回调
3.3.3 添加与消息相关的路由
创建模板文件post.ejs
<form action="/post" method="post"> <p> <input type="text" name="entry[title]" placeholder="Title" /> </p> <p> <input type="text" name="entry[body]" placeholder="Body" /> </p> <p> <input type="submit" value="Post"> </p> </form>
然后建立路由
app.get('/post', entries.form) app.post('/post', entries.submit) /* 下面是routes/entries文件 */ // GET '/post' exports.form = (req, res, next) => { res.render('post', { title: 'post' }); } // POST '/post' exports.submit = (req, res, next) => { console.log(req.body.entry) res.redirect('/') // res.render('post', { title: 'post' }); }
还要注意的地方,得打开bodyParse解析
app.use(express.urlencoded({ extended: true }));
将消息存入数据库中
exports.submit = (req, res, next) => { const data = req.body.entry const entry = new Entry({ username: 'zmj', title: data.title, body: data.body }) entry.save((err) => { if (err) return next(err) res.redirect('/') }) }
显示所有消息
// GET '/list' exports.list = (req, res, next) => { Entry.getRange(0, -1, (err, entries) => { console.log(entries) res.render('entries', { title: 'msg list', entries: entries }) }) }
3.3.4 校验表单
说起校验表单,你可能第一种想的方式是在这里面完成
// POST '/post' exports.submit = (req, res, next) => { const data = req.body.entry const entry = new Entry({ username: 'zmj', title: data.title, body: data.body }) entry.save((err) => { if (err) return next(err) res.redirect('/') }) }
这种方式扩展性不好,我们使用特定的中间件来实现校验, 我们创建一个中间件middleware/validate.js
const parseField = (field) => { // 将 entry[filed] 解析为 ['entry', 'filed'] return field.split(/\[|\]/g).filter((s) => s) } const getField = (req, field) => { // 这个骚操作,我醉了! 首先val = req.body, 然后val = val[entry], 然后val = entry.filed let val = req.body field.forEach((prop) => { val = val[prop] }) return val } exports.required = (field) => { field = parseField(field) return (req, res, next) => { if (getField(req, field)) { next() } else { res.render('error,你必须输入标题') res.redirect('back') } } } exports.lengthAbove = (field, len) => { field = parseField(field) return (req, res, next) => { if (getField(req, field).length > len) { next() } else { res.render(`error,你内容必须超过${len}个长度`) res.redirect('back') } } }
这段代码理解起来有难度,但这很方便我们使用中间件,以及添加各种验证方式
使用验证表单的中间件
app.post('/post', validate.required('entry[title]'), validate.lengthAbove('entry[body]', 5), entries.submit)
我们只需要,将表单字段传过来即可验证,是不是很方便,扩展性也相当好!
该节总结
这一段实现了留言版的功能,使用redis当数据库
可以留言,对留言进行了简单的校验
这一节做了三个路由
- GET /post: 显示消息表单
- POST /post: 提交消息表单
- GET /list: 获取消息列表
总言之,学到了不少东西。
很多东西都是书上没写需要自己去手动查资料的,像express的一些api,redis的使用等等