(1)反馈
(2)项目结构
1、新建项目文件目录forum(社区)
2、初始化项目,新建项目说明文件package.json
3、初始化git仓库,用于后期向git推送发布
4、新建项目说明文档README.md,后期在Git上也可以浏览
5、创建git上传忽略文件类型设置文件“.gitignore”
6、安装核心包Express(开启创建服务)、mongoose(数据库)
下载完毕后会发现,npm新版在安装第三方依赖时,会自动创建package-lock.json锁文件,将记录依赖树,锁定版本
7、创建静态资源开放目录public,给客户端用
8、创建入口文件,编写代码
注意:这里需要开放静态资源public,同时因为后期要用jquery和bootstrap,所以这里开放node_modules目录,让前端直接访问该目录下的资源
查看Express官方文档会发现开放静态资源有个写法
这里可以对上述代码做下修改
这里注意:path为核心模块(路径操作模块),所以需要引入,接下来便可以通过url浏览静态资源。如下所示
上面path.join(__dirname,'./public')与之前相比,是将路径变为绝对路径。这里 便涉及到两个知识点:path路径操作模块和__dirname文件路径。接下来做下介绍
(3)path路径操作模块
详情参见path路径操作模块.
(4)dirname和filename
详情参见node中其他成员(非模块成员)之dirname和filename.
(5)art-template模板引擎中的include-extend-block
1、接下来开始渲染页面,此时需要配合模板引擎
因为这里用的Express搭建的服务,所以需要用到模板引擎express-art-template,同时还需要art-template。因为express-art-template是基于art-template开发的
新版npm5.xxx之后不需要加--save参数,即可默认加上
2、接下来启动服务,配置模板引擎相关参数
注意:这里避免相对路径,使用动态绝对路径
index.html内容:
启动后,测试如下
因为放在了public开放目录下,所以也可以通过开放资源url访问
接下来加个模板参数进行渲染
3、模板参数渲染
结果如下所示
对比分析可得静态资源经过了模板编译,此时渲染成了我们想要的内容
4、公共部分抽离
分析可得在开发中,有的地方例如网页头部导航和底部,在很多页面用到,此时便可以抽取出来
接下来打开art-template官网文档,查看相关配置(这里选择简体中文,方便阅读)
然后查找语法
接下来看下子模板和模板继承
①首先新建文件header.html
②接下来在其他页面(同级index.html)引入
③同理,建立公共底部模板文件footer.html
渲染结果如下
④进一步封装,目前为止已经把头部、底部抽离出去,但还有很多架构一样
此时便可以利用模板继承
⑤模板继承
接下来新建布局模板layout.html,将原来的index.html文件内容粘贴过去做下修改
接下来在index.html页面去继承
此时页面渲染结果如下
分析:因为index.html没有填入内容,所以这里展示默认内容
⑥接下来开始填坑
分析可知:填坑内容与留坑语法类似,此时结果如下
⑦公共模板完善
因为接下来要用到bootstrap和jquery,所以直接在公共模板页面引入,首先下载第三方依赖包
补充(dist发布目录):
查看效果,发现文件引入成功,字体样式已经改动
此时模板页和后代子页面都已经整合到一起
5、art-template多环境应用
模板引擎art-template可以在node服务器环境应用,也可以在浏览器环境应用,原因在于源码里没有涉及具体环境API(模板引擎只操作字符串,与DOM操作、文件操作均无关)
6、其他相关模板引擎
7、公共模板页的设计
关于公共模板页layout.html的设计主要注意: 1、将公共部分放到这里 2、在需要后代继承覆盖的地方留坑,并加上“默认内容”
为了后代各个页面拥有自己的样式和脚本,所以还需要在样式和脚本处留坑,但这里注意:默认位置,后期会被覆盖,也可以不填,之后再做修改
接下来在index.html对应填坑即可
对于脚本部分,为了防止混淆,我们可以命名为script
这里对于留坑位置做下修改,都给去掉
然后在index页面做下样式定制
此时网页记载完毕后的内容如下
(6)案例资源页面
这里便涉及到文件分类和命名,之所以用_,是因为要和其他业务主页面区分开来
对于公共页面局部,可以抽离归类到相关文件夹,例如_layouts(布局)目录,存放如下(因为可能有多个公共模板,所以命名为layouts)
_partials(部分)目录,存放如下,存放公共头部、尾部、导航等
所以接下来依次编写静态页面,即静态资源页面设计和命名,详见node(day6)之静态资源页面设计和命名.
(7)设计注册、登录、退出路由
相对之前来说多了几个目录controllers、models、routes等。
接下来设计下路由
注意:有些功能是需要权限的,例如发布一篇博客,如果不登录是无法发表的
(8)配置注册登录路由
1、路由提取
正如之前所讲,入口文件不做路由处理
一般小型项目,可以直接在根目录下新建router.js处理路由。
但当项目较大,请求越来越多时,最好将其在routes目录下分类管理。
例如:注册登录退出相关的操作,放到routes下的session.js(会话),因为注册登录退出操作,都相当于在和服务器进行会话
新建话题、删除话题、修改话题、查看话题列表等操作,放到routes下的topic.js
例如:这里在routes/router.js设计渲染首页路由,如下所示( 备注:后期修改为routes下各个子文件操作,然后通过routes/router.js统一管理 )
接下来在入口文件require引入加载并use使用
2、接下来添加其他路由操作
之后进行验证,点击登录、注册按钮跳转到相应页面即可
(9)快速启动配置
为了快速启动项目,接下来在项目说明文件配置scripts项,如下所示
配置完毕后,以后再启动项目只需输入指令“npm start”
(10)处理注册页面,配置Express中间件body-parser
接下来看下注册页模板内容
点击提交时利用jQuery操作表单提交,所以这里需要引入jquery
serialize() 方法通过序列化表单值创建 URL 编码文本字符串,
序列化的值可在生成 AJAX 请求时用于 URL 查询字符串中
关于jQuery的ajax里dataType预期服务器返回数据类型,详见jQuery的ajax里dataType预期服务器返回数据类型.
这里的data为序列化后的表单数据,打印如下
接下来获取表单post提交数据,这时便需要配置body-parser中间件
1、下载body-parser中间件
2、在入口文件进行相关配置,注意:一定要放在挂载路由之前配置
此时在路由配置文件输出打印post提交数据
测试如下:
如果body-parser配置放到了挂载路由到app实例之前,则获取不到post提交数据
接下来便要开始操作数据库,这里我们使用mongoose第三方包实现,首先要设计数据模型
(11)设计数据库(用户数据模型)
1、分析
因为除了用户数据,还有话题相关数据、评论相关数据等,所以这里需要设计多个数据模型,将其放到models目录下
models下有多个数据模型,代表多个集合。user.js代表用户数据集合
2、设计user.js用户数据集合
注意:集合命名为大写单数User,最终数据库集合名会变为小写复数users
3、设计文档格式
首先是基本信息
另外,除了基本信息,还有些看不到的信息,例如创建时间、修改时间... ...,这里加入创建即注册时间
重点分析:
这里可能会有疑问:为什么Date.now没有加函数执行符?
原因:加入函数执行符会即时调用
这里传入方法Date.now,当你去new Model时即实例化模型,如果没有传递create_time,则mongoose就会调用default指定的Date.now方法使用其返回值作为默认值
对比分析:例如type为a+b,则在new Schema时便自动计算出结果
同理,在创建时间处,如果加入函数执行符,则会立即执行。这时变成了写死的时间
接下来设计修改时间文档格式
此外还有用户头像avatar,在用户信息设置处有个默认头像
此外,在用户信息设置里还有介绍bio
加下来还有性别gender
接下来还有生日,生日这里不用加默认值
接下来结合业务逻辑需求,看下还需要哪些数据,如下所示
分析完毕后,此外还有账户状态status。例如管理员管理用户状态,禁言、封号等等... ...
此外也可以通过mongoose框架模型层次,对数据格式做限制,例如email必须为邮箱格式、昵称nickname长度不能超过10位等等... ...,需要用到验证中间件,后期介绍
(12)处理注册请求
1、开启数据库服务,连接数据库
2、基于之前设计的用户数据模型,进行业务操作(注册、登录等)
注册即为保存数据、登录为查询数据
在正式操作前,首先在编码里连接数据库
目前为止先写到这里,但操作其他数据时(topic.js、comment.js)还需要再次连接,肯定不合适
所以这里做下修改,新建mongoose.js操作数据库,如下所示
/*1、引包*/ var mongoose = require('mongoose') var db_url = 'mongodb://localhost/blog' /*2、连接数据库*/ /*mongoose.connect('mongodb://localhost:27017/0204')默认开启的端口为27017*/ mongoose.connect(db_url)/*这里也可以将端口去掉,默认便是27017*/ /* 链接成功 */ mongoose.connection.on('connected', function() { console.log('Mongoose connection open to ' + db_url); }); /* 链接异常 */ mongoose.connection.on('error', function(error) { console.log('Mongoose connection error:' + err); }); /* 链接断开 */ mongoose.connection.on('disconnected', function() { console.log('Mongoose connection disconnected'); }); module.exports = mongoose;
之后在其他文件调用即可
首先查询用户邮箱是否存在,然后查询用户昵称是否存在。这里我们使用fAPI为indOne,find方法即使只查出一个,也会将其放入数组
如果邮箱已经存在,接下来需要判断昵称,查询文档,找到or操作
所以接下来利用or
接下来直接判断“邮箱或者昵称是否重复。如果两者有一个存在则提示,邮箱或者昵称已存在”
当然可能有的网站在切换输入框时便直接判断,这样的话需要将邮箱、昵称分开编写API接口
注意:这里res.send()返回结果需要结合客户端代码进行分析。
如下所示,查看后即可得知,该操作是表单操作post异步请求,需要服务端返回JSON数据,之后客户端根据JSON内容进行业务交互
接下来做下测试
此时发现客户端没有输出,服务端执行到了OK。
那么问题来了:服务端发送了响应数据,为什么客户端没有输出呢?(服务端发送了响应数据为ok,如下所示)
原因:客户端ajax里指定期望服务器返回数据格式dataType为json,但服务端发送的不是json。所以客户端解析不到,所以客户端没有报错,也看不见
接下来做下测试,给客户端发送响应数据,格式为json
此时在做下测试,点击注册按钮后,便可以发现客户端打印数据
分析:jQuery的ajax里的dataType:json的作用为
预期后台返回结果为json字符串,如果返回的是json字符串,那么直接将其解析转为js可操作的对象,结合文章jQuery的ajax里dataType预期服务器返回数据类型.理解
如果后台返回结果为字符串,而不是json格式字符串,那么无法进行转换,如下所示
如果这里是纯字符串,那么肯定无法正常转换。但客户端里面尝试转换对象格式失败,虽然无法正常输出,但也不会报错
3、后台发送json格式响应
如下所示,直接发送json格式响应
所以这里使用JSON.stringify()进行转换
此时注册页面提交后便可以输出后台发送的响应
4、Express框架的json方法
再在响应里添加数据,测试如下
都可以正常获取服务器返回响应,但是这样操作也过于繁琐,Express封装了json方法可以直接转换
测试如下:
Express的响应方法json方法会自动将里面的对象转换为json字符串,也就免去了手动转换
小结:
5、json响应方法替换send
接下来将之前的send响应方法都替换为json,然后统一响应格式.
这里注意:error中属于服务器错误所以success为false,而邮箱或者昵称已经存在属于业务操作,都已经完成对比,所以状态码为200
6、保存数据到数据库
如果数据没有问题,则保存数据到数据库
接下来做下测试,注册页面填写信息提交,然后连接数据库进行查询
再次点击注册,客户端输出结果如下(邮箱或昵称已存在email or nickname already exist)
7、服务端自定义业务状态码
接下来开始在客户端根据服务端返回响应进行操作,但此时仍然存在问题
即返回状态码都为200或者500,无法准确获取响应结果。此时便需要在服务端设计一些自定义的业务状态码... ...
虽然也可以根据字符message判断,但字符可控性太差,所以还是推荐自定义业务状态码
接下来将success改为error_code,然后自定义值
500为错误,1为邮箱或昵称已存在,0位成功... ...
因此这里便需要接口文档进行前后端协同开发,统一规定自定义业务状态码
8、客户端接收响应,针对性处理
测试如下(再次提交时便会提示,信息已经存在)
此时可以用CMD或者MongoDB的可视化操作工具进行预览,接下来可以启动预览
CMD操作如下
9、密码加密md5
此时可以从数据库直接看到密码,但像密码这种隐私数据,一般存储时都是md5加密(php、node、java等任何语言都可以加密操作)
目的:防止数据库信息泄露,用户密码曝光
这里便需要用到第三方包,Github有很多种,选择一种即可
这里例如选择第一个
这里附上GitHub中文社区,进入直接搜索md5即可GitHub中文社区
接下来开始安装使用
接下里引入使用
然后在存储数据之前对密码进行二次加密,当然也可以多层加密,提高安全性。一层加密的话加密程度低,建议至少两层
接下来做下测试(密码为123456)
注册成功后查看数据库,此时发现密码为加密后的数据
注意:
登录时输入的密码不能直接和数据库存储的加密密码比对,都是拿着加密的密码进行比对。
且只能正向加密,不能反向解密。所以,即使是开发人员,也无从得知用户密码
(13)表单同步提交和异步提交
1、分析
从特性上将,表单具有默认的提交行为,默认是同步的,即同步表单提交,浏览器会锁死(转圈... ...),等待服务端的响应结果
接下来做下对比分析
2、异步提交,首先看下案例里的异步提交
异步表单提交:form标签内部不再编写action和method,而是通过ajax的url和method选项去实现提交
3、同步提交
同步提交时不再需要ajax,而是直接在form表单的开始标签里添加action和method属性实现,接下来开始进行注册
点击提交按钮
再次刷新页面,内容如下
4、区别
同步提交表单内容会导致浏览器锁死,异步提交表单内容不会发生锁死,浏览器任然可以干别的事情
详情参见文章浅谈表单同步提交和异步提交.
(14)服务端重定向,针对异步请求无效
当注册成功后,应该重定向到登录页面
目前为止,只是弹框提示
注意:如果客户端是异步请求,服务端重定向针对异步请求无效。如下所示,如果改为重定向或者res.render()、res.send()都会无效,无法跳转重定向。
即如果是异步请求服务端无法重定向,对比之前的表单同步请求则可以实现跳转重定向
所以如果想实现重定向,必须由客户端实现。如下所示
关于页面重定向知识点,如下所示
(15)登录功能
1、首先编写html模板代码
接下来编写脚本代码,获取表单输入提交内容,并且阻止表单默认提交事件
测试如下
2、发送AJAX请求,执行异步提交操作
注意:一般为了防止他人暴力破解,不会专门提示邮箱错误或者密码错误。
场景:他人在输入邮箱和密码后提示密码错误,则可以知道,该账号一定存在,接下来只破解密码即可,增加了用户账号风险。如下所示
所以一般会提示“邮箱或者密码错误”,所以编写ajax请求如下
3、处理登录操作服务端
登录界面渲染已经没有问题即get请求已经解决,接下来主要处理表单post请求
4、处理登录post请求
步骤:获取请求数据→查询数据库→发送响应
首先获取表单数据,接下来链接数据库查询,因为之前已经加载过了用户数据设计文档
所以接下来通过User操作即可
注意: 1、可以在读取失败时直接将错误信息发送回去,通过error自带的属性,即error.message属性。此时客户端在开发时,便可以看到详细报错信息 2、因为是异步请求,服务端无法重定向到首页,所以需要发送成功响应,自定义状态码 3、用户存在,登录成功,通过Session记录登录状态(稍后添加)
测试如下
点击确定后便会重定向到首页,接下来做下session登录状态存储。
(16)通过session保存登录状态
详见文章node通过session保存登录状态.
(17)处理用户退出功能
接下里编写退出功能,即点击退出按钮时进行用户退出操作
接下来编写路由
注意:a链接默认为同步请求,因为点击链接时,浏览器便会刷新。
因为是同步请求,所以可以使用服务端重定向。注意:之前所讲,ajax异步请求的重定向,无法在服务端操作。
(18)小结
(19)总结
.