内容简介:下面开始用 Node.js 进行 Web 开发。我是通过《Node.js开发指南》这本书来学习 Node.js Web 开发的,书中使用的 Express 框架是 2.5.8,而我的是 4.14.1,所以遇到了许多问题,在文章中我都有提到并讲解。☞
下面开始用 Node.js 进行 Web 开发。
我是通过《Node.js开发指南》这本书来学习 Node.js Web 开发的,书中使用的 Express 框架是 2.5.8,而我的是 4.14.1,所以遇到了许多问题,在文章中我都有提到并讲解。
一、快速开始
1、建立项目
《Node.js开发指南》中建立项目的方式是:express -t ejs microblog,但是这种方式对于高版本的 Express 新建的标签替换引擎并不是 .ejs,而是 .jade,如果要使用 .ejs 我们可以通过下面命令建立网站基本结构。
express -e NodeJSBlog 复制代码
执行命令后在当前目录下出现了一些文件,并且下边提示我们通过 npm install 安装依赖。
在 npm install 之后,打开 NodeJSBlog 目录下的 package.json,可以看到已安装的包及对应的版本号。
2、启动服务器
注意,我们之前开启 Node.js 服务器,都是执行 node xxx.js,然后去浏览器访问即可,但是 Express 4.x 以上就不是这种方式了,应该是 npm start,端口配置在 bin/www 中。
启动成功访问 localhost:3000/。
3、项目结构
我们看一下 express 在 NodeJSBlog 这个目录下都生成了哪些文件。
app.js
var express = require('express'); var path = require('path'); var favicon = require('serve-favicon'); var logger = require('morgan'); var cookieParser = require('cookie-parser'); var bodyParser = require('body-parser'); var index = require('./routes/index'); var users = require('./routes/users'); var app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); app.use('/', index); app.use('/users', users); // catch 404 and forward to error handler app.use(function(req, res, next) { var err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function(err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app; 复制代码
app.js 是项目的入口,首先引入了一系列我们所需要的模块,然后引入了 routes 目录下的两个本地模块,它的功能是为指定路径组织返回内容,相当于 MVC 架构中的控制器。
接下来是视图引擎设置, app.set() 是 Express 的参数设置工具,接受一个键(key)和一个值(value),可用的参数如下所示:
- basepath:基础地址,通常用于 res.redirect() 跳转。
- views:视图文件的目录,存放模板文件。
- view engine:视图模板引擎。
- view options:全局视图参数对象。
- view cache:启用视图缓存。
- case sensitive routes:路径区分大小写。
- strict routing:严格路径,启用后不会忽略路径末尾的“ / ”。
- jsonp callback:开启透明的 JSONP 支持
Express 依赖于 connect,提供了大量的中间件,可以通过 app.use() 启用
routes/index.js
var express = require('express'); var router = express.Router(); /* GET home page. */ router.get('/', function(req, res, next) { res.render('index', { title: 'Express' }); }); module.exports = router; 复制代码
routes/index.js 是路由文件,相当于控制器,用于组织展示的内容,app.js 中通过 app.get('/', routes.index); 将“ / ”路径映射到 exports.index 函数下,其中只有一个语句 res.render('index', { title: 'Express' }),功能是调用模板解析引擎,翻译名为 index 的模板,并传入一个对象作为参数,这个对象只有一个属性,即 title: 'Express'。
views/index.ejs
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body> </html> 复制代码
index.ejs 是模板文件,即 routes/index.js 中调用的模板,内容是:
<h1><%= title %></h1> <p>Welcome to <%= title %></p> 复制代码
它的基础是 HTML 语言,其中包含了形如 <%= title %> 的标签,功能是显示引用的变量,即 res.render 函数第二个参数传入的对象的属性。
补充 include
在书中 views 目录下是有 layout.ejs 的,它可以让所有模板去继承它,<%- body %> 中是独特的内容,其他部分是共有的,可以看作是页面框架。
书中 layout.ejs:
<head> <title> <%= title %> </title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <%- body %> </body> </html> 复制代码
但是 Express 4.x 就没有 layout.ejs 了,解决方法:
官方推荐了 include 方式,它不仅能实现 layout 的功能,还是将 view 的那些可复用的 html 片段提取成模块,在需要使用的地方直接用 <% include xxx %>。
例如先在 views 目录下新建一个 public_file.ejs ,在里面添加需要引用的公共文件:
<link rel='stylesheet' href='/stylesheets/style.css' /> 复制代码
然后修改一下 index.ejs,使用 <% include listitem %> 方式引用上边公共文件:
<!DOCTYPE html> <html> <head> <title><%= title %></title> <% include public_file %> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> </body> </html> 复制代码
重启服务,访问 localhost:3000/,可以看到 style.css 文件正常引用。
每一次修改我们都需要重启服务才能看到修改后的结果,您可以看我的 《Node.js 应用程序自动重启》 这篇文章去安装 supervisor 或 nodemon 两个插件来实现应用程序自动重启。
二、路由控制
1、创建页面路由
简单说一下新增一个页面的流程。
首先在 views 目录下新建一个模板,例如 hello.ejs:
然后打开 index.js 文件,添加页面路由信息:
访问 localhost:3000/hello。
补充:在《Node.js开发指南》这本书中,还需要向 app.js 文件中添加页面路由信息,但在 Express 4.x 中是不需要的。
2、路径匹配
上面的例子是为固定的路径设置路由规则,Express 还支持更高级的路径匹配模式,例如我们想要展示一个用户的个人页面,路径为 /user/[username],可以用下面的方法定义路由 规则:
router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); 复制代码
重启项目,访问 localhost:3000/user/LiuZhenghe。
注意:调用模板解析引擎,用 res.render(),只是向页面发送数据,用 res.send()。
路径规则 /user/:username 会被自动编译为正则表达式,类似于 /user/([^/]+)/? 这样的形式,路径参数可以在响应函数中通过 req.params 的属性访问。
路径规则同样支持 JavaScript 正则表达式,例如 app.get(/user/([^/]+)/?,callback),这样的好处在于可以定义更加复杂的路径规则,而不同之处是匹配的参数是匿名的,因此需要通过 req.params[0]、req.params[1] 这样的形式访问。
3、REST 风格的路由规则
Express 支持 REST 风格的请求方式,在介绍之前我们先说明一下什么是 REST。
REST 的意思是表征状态转移(Representational State Transfer),它是一种基于 HTTP 协议的网络应用的接口风格,充分利用 HTTP 的方法实现统一风格接口的服务。
HTTP 协议定义了以下 8 种标准的方法:
- GET:请求获取指定资源。
- HEAD:请求指定资源的响应头。
- POST:向指定资源提交数据。
- PUT:请求服务器存储一个资源。
- DELETE:请求服务器删除指定资源。
- TRACE:回显服务器收到的请求,主要用于测试或诊断。
- CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
- OPTIONS:返回服务器支持的HTTP请求方法。
其中我们经常用到的是 GET、POST、PUT 和 DELETE 方法,根据 REST 设计模式,这4种方法通常分别用于实现以下功能。
- GET:获取
- POST:新增
- PUT:更新
- DELETE:删除
这是因为这 4 种方法有不同的特点,按照定义,它们的特点如下表所示:
请求方式 | 安全 | 幂等 |
---|---|---|
GET | 是 | 是 |
POST | 否 | 否 |
PUT | 否 | 是 |
DELETE | 否 | 是 |
所谓安全是指没有副作用,即请求不会对资源产生变动,连续访问多次所获得的结果不受访问者的影响,而幂等指的是重复请求多次与一次请求的效果是一样的,比如获取和更新操作是幂等的,这与新增不同,删除也是幂等的,即重复删除一个资源,和删除一次是一样的。
Express 对每种 HTTP 请求方法都设计了不同的路由绑定函数,例如前面例子全部是 app.get,表示为该路径绑定了 GET 请求,向这个路径发起其他方式的请求不会被响应。
下表是 Express 支持的所有 HTTP 请求的绑定函数。
请求方式 | 绑定函数 |
---|---|
GET | app.get(path, callback) |
POST | app.post(path, callback) |
PUT | app.put(path, callback) |
DELETE | app.delete(path, callback) |
PATCH | app.patch(path, callback) |
TRACE | app.trace(path, callback) |
CONNECT | app.connect(path, callback) |
OPTIONS | app.options(path, callback) |
所有方法 | app.all(path, callback) |
例如我们要绑定某个路径的 POST 请求,则可以用 app.post(path, callback) 的 方法,需要注意的是 app.all 函数,它支持把所有的请求方式绑定到同一个响应函数,是一个非常灵活的函数,在后面我们可以看到许多功能都可以通过它来实现。
4、控制权转移
Express 支持同一路径绑定多个路由响应函数,例如:
index.js
// ... /* 路径匹配模式 */ router.all('/user/:username', function(req, res, next) { res.send('all methods captured'); }); router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); // ... 复制代码
当再次访问 localhost:3000/user/LiuZhenghe 时,发现页面被第一条路由规则捕获。
原因是 Express 在处理路由规则时,会优先匹配先定义的路由规则,因此后面相同的规则被屏蔽。
Express 提供了路由控制权转移的方法,即回调函数的第三个参数 next,通过调用 next(),会将路由控制权转移给后面的规则,例如:
// ... /* 路径匹配模式 */ router.all('/user/:username', function(req, res, next) { console.log('all methods captured'); next(); }); router.get('/user/:username', function(req, res, next) { res.send('user: ' + req.params.username); }); // ... 复制代码
此时刷新页面,在控制台可以看到“all methods captured”,浏览器显示了 user: LiuZhenghe。
这是一个非常有用的工具,可以让我们轻易地实现中间件,而且还能提高代码的复用程度,例如我们针对一个用户查询信息和修改信息的操作,分别对应了 GET 和 PUT 操作,而两者共有的一个步骤是检查用户名是否合法,因此可以通过 next() 方法实现。
三、模板引擎
模板引擎也就是视图,视图决定了用户最终能看到什么,这里我们用 ejs 为例介绍模板引擎的使用方法。
1、使用模板引擎
在 app.js 中,以下两句设置了模板引擎和页面模板的位置:
app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs'); 复制代码
在 index.js 中通过 res.render() 调用模板,res.render() 的功能是调用模板引擎,并将其产生的页面直接返回给客户端,它接受两个参数,第一个是模板的名称,即 views 目录下的模板文件名,不包含文件的扩展名;第二个参数是传递给模板的数据,用于模板翻译。
ejs 的标签系统非常简单,它只有以下 3 种标签:
- <% code %>:JavaScript 代码。
- <%= code %>:显示替换过 HTML 特殊字符的内容。
- <%- code %>:显示原始 HTML 内容。
我们可以用它们实现页面模板系统能实现的任何内容。
2、片段视图
《Node.js开发指南》中所讲的片段视图(partials)在 Express 4.x 中已经不支持了,在上面项目结构分析那一节中我曾补充过 include,这里再次介绍一下它的用法。
官方推荐了 include 方式,它不仅能实现 layout 的功能,还是将 view 的那些可复用的 html 片段提取成模块,在需要使用的地方直接用 <% include xxx %>,看下面这个例子:
首先在 index.js 中新增以下内容:
// 片断视图 router.get('/list', function(reg, res) { res.render('list', { title: "List", items: [2019, 'Node.js', 'NodeJSBlog', 'Express'] }); }); 复制代码
然后新建 list.ejs 文件并添加以下内容:
<ul> <% items.forEach(function(listitem){ %> <% include listitem %> <% }) %> </ul> 复制代码
同时新建 listitem.ejs 文件并添加:
<li><%= listitem %></li> 复制代码
访问 localhost:3000/list,可以看到以下内容:
四、开始建立博客网站
1、功能分析
博客网站首先应该有登录注册功能,然后是最核心的功能——信息发表,这个功能涉及到许多方面,包括数据库访问,前端显示等。
一个完整的博客系统,应该有评论,收藏,转发等功能,处于本人目前的能力水平还不能都实现,先做一个博客网站的雏形吧。
2、路由规划
根据功能设计,我们把路由按照以下方案规划:
- /:首页
- /u/[user]:用户的主页
- /post:发表信息
- /reg:用户注册
- /login:用户登录
- /logout:用户登出
以上页面还可以根据用户状态细分,发表信息以及用户登出页面必须是已登录用户才能操作的功能,而用户注册和用户登入所面向的对象必须是未登入的用户,首页和用户主页则针对已登入和未登入的用户显示不同的内容。
在 index.js 中添加以下内容:
router.get('/', function(req, res) { res.render('index', { title: 'Express' }); }); router.get('/u/:user', function(req, res) {}); router.post('/post', function(req, res) {}); router.get('/reg', function(req, res) {}); router.post('/reg', function(req, res) {}); router.get('/login', function(req, res) {}); router.post('/login', function(req, res) {}); router.get('/logout', function(req, res) {}); 复制代码
其中 /post、/login 和 /reg 由于要接受表单信息,因此使用 router.post 注册路由,/login 和 /reg 还要显示用户注册时要填写的表单,所以要以 router.get 注册。
3、使用 Bootstrap
下载 jquery.js,bootstrap.css 和 bootstrap.js,放到 public 下对应的目录中。
在 public_file.ejs 中引用,可以使用 include 方法给模板添加公共文件。
去Bootstrap官网 查看并使用所需的模板或组件。
下图是我从 Bootstrap 官网找的一个模板,并放到了 index.ejs 目录下并进行了简单地修改,并将页面公共部分(头尾部分)取出,用 include 方式来复用。
五、用户注册和登录
1、访问数据库
我们选用 MongoDB 作为网站的数据库系统,它是一个开源的 NoSQL 数据库,相比 MySQL 那样的关系型数据库,它更为轻巧、灵活,非常适合在数据规模很大、事务性不强的场合下使用。
连接数据库
通过 npm 安装 mongodb。
npm install mongodb --save 复制代码
补充:通过 --save 安装,包名和版本号将会出现在 package.json 中。
接下来在项目主目录中创建 settings.js 文件,这个文件用于保存数据库的连接信息,我们将用到的数据库命名为 NodeJSBlog,数据库服务器在本地,因此 settings.js 文件的内容如下:
settings.js
module.exports = { cookieSecret: 'NodeJSBlogbyvoid', db: 'NodeJSBlog', host: 'localhost', }; 复制代码
其中,db 是数据库的名称,host 是数据库的地址,cookieSecret 用于 Cookie 加密与数据库无关,我们留作后用。
接下来新建 models 目录,并在目录中创建 db.js:
models/db.js
var settings = require('../settings'), Db = require('mongodb').Db, Connection = require('mongodb').Connection, Server = require('mongodb').Server; module.exports = new Db(settings.db, new Server(settings.host, 27017, {}), { safe: true }); 复制代码
以上代码通过 module.exports 输出了创建的数据库连接,在后面的小节中我们会用到这个模块,由于模块只会被加载一次,以后我们在其他文件中使用时均为这一个实例。
2、会话支持
《Node.js开发指南》中对会话支持是这样描述的:
会话是一种持久的网络协议,用于完成服务器和客户端之间的一些交互行为。会话是一个比连接粒度更大的概念,一次会话可能包含多次连接,每次连接都被认为是会话的一次操作。在网络应用开发中,有必要实现会话以帮助用户交互。例如网上购物的场景,用户浏览了多个页面,购买了一些物品,这些请求在多次连接中完成。许多应用层网络协议都是由会话支持的,如 FTP、Telnet 等,而 HTTP 协议是无状态的,本身不支持会话,因此在没有额外手段的帮助下,前面场景中服务器不知道用户购买了什么。
为了在无状态的 HTTP 协议之上实现会话,Cookie 诞生了。Cookie 是一些存储在客户端的信息,每次连接的时候由浏览器向服务器递交,服务器也向浏览器发起存储 Cookie 的请求,依靠这样的手段服务器可以识别客户端。我们通常意义上的 HTTP 会话功能就是这样实现的。具体来说,浏览器首次向服务器发起请求时,服务器生成一个唯一标识符并发送给客户端浏览器,浏览器将这个唯一标识符存储在 Cookie 中,以后每次再发起请求,客户端浏览器都会向服务器传送这个唯一标识符,服务器通过这个唯一标识符来识别用户。
对于开发者来说,我们无须关心浏览器端的存储,需要关注的仅仅是如何通过这个唯一标识符来识别用户。很多服务端脚本语言都有会话功能,如 PHP,把每个唯一标识符存储到文件中。Express 也提供了会话中间件,默认情况下是把用户信息存储在内存中,但我们既然已经有了 MongoDB,不妨把会话信息存储在数据库中,便于持久维护。
但是如果你的 Express 版本是 4.x,按照书中接下来的内容编写代码,就会出各种问题,下面我来说一下 Express 4.x 中的会话支持。
在 Express 4.x 中我们需要自己安装 express-session 包,然后添加引用:
var session = require('express-session'); 复制代码
然后再引用 connect-mongo 包:
var MongoStore = require('connect-mongo')(session); 复制代码
最终在 app.js 中新增的内容就是:
var settings = require('./settings'); var session = require('express-session'); var MongoStore = require('connect-mongo')(session); app.use(session({ secret: settings.cookieSecret, store: new MongoStore({ db: settings.db, }) })); 复制代码
但是此时程序会报错:'Connection strategy not found':
从网上查找到该问题之后找到了解决办法,打开 package.json 文件,将 connect-mongo 的版本改为 0.8.2,然后执行 npm install 即可解决。
如果程序没有报错,那么就成功了,你可以使用 mongo.exe 或可视化 工具 来查看数据库是否新建成功。
3、注册和登录
3.1、注册
通过可视化工具或 mongodb 终端新建一个用户表 users。
注册页面:
首先添加注册页面的模板 views/reg.ejs,并添加表单结构:
<div class="container"> <div class="bs-example" data-example-id="basic-forms"> <form> <div class="form-group"> <label for="username">用户名</label> <input type="text" class="form-control" id="username" placeholder="请输入用户名"> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" id="password" placeholder="请输入密码"> </div> <div class="form-group"> <label for="password_repeat">再次输入密码</label> <input type="password" class="form-control" id="password_repeat" placeholder="再次输入密码"> </div> <button type="submit" class="btn btn-default">注册</button> </form> </div> </div> 复制代码
然后打开 index.js 添加注册页面的路由信息:
index.js
router.get('/reg', function(req, res) { res.render('reg', { title: '用户注册' }); }); 复制代码
然后访问 localhost:3000/reg。
注册响应:
在书中使用了 flash,但是最新版本的 Express 已经不支持 flash 了,你需要先通过 npm 安装 connect-flash。
然后在 app.js 中添加如下代码:
var flash = require('connect-flash'); 复制代码
在 routes/index.js 中添加 /reg 的 POST 响应函数:
routes/index.js
// 注册响应 var crypto = require('crypto'); var User = require('../models/user.js'); var MongoClient = require('mongodb').MongoClient; const DB_CONN_STR='mongodb://localhost:27017/users'; router.post('/reg', function(req, res) { let newUser = { username: req.body.username, password: req.body.password, password_repeat: req.body.password_repeat }; let addStr = [{ username: newUser.username, password: newUser.password }]; MongoClient.connect(DB_CONN_STR, function(err, db) { db.collection('users').findOne({ username: newUser.username }, function(err, result) { if (!result) { if (newUser.password === newUser.password_repeat) { MongoClient.connect(DB_CONN_STR, function(err, db) { req.session.error = '注册成功,请登录!'; db.collection('users').insert(addStr); db.close(); return res.redirect('/login'); }); } else { req.session.error = '两次密码不一致!'; return res.redirect('/register'); } } else { req.session.error = '用户名已存在!'; return res.redirect('/register'); } }) db.close(); }); }); 复制代码
- req.body 就是 POST 请求信息解析过后的对象,例如我们要访问用户传递的 password 域的值,只需访问 req.body['password'] 即可。
- req.flash 是 Express 提供的一个奇妙的工具,通过它保存的变量只会在用户当前和下一次的请求中被访问,之后会被清除,通过它我们可以很方便地实现页面的通知和错误信息显示功能。
- res.redirect 是重定向功能,通过它会向用户返回一个 303 See Other 状态,通知浏览器转向相应页面。
- crypto 是 Node.js 的一个核心模块,功能是加密并生成各种散列,使用它之前首先要声明 var crypto = require('crypto'),我们代码中使用它计算了密码的散列值。
- User 是我们设计的用户对象,在后面我们会详细介绍,这里先假设它的接口都是可用的,使用前需要通过 var User = require('../models/user.js') 引用。
- User.get 的功能是通过用户名获取已知用户,在这里我们判断用户名是否已经存在,User.save 可以将用户对象的修改写入数据库。
- 通过 req.session.user = newUser 向会话对象写入了当前用户的信息,在后面我们会通过它判断用户是否已经登录。
用户模型
在前面的代码中,我们直接使用了 User 对象,User 是一个描述数据的对象,即 MVC 架构中的模型,前面我们使用了许多视图和控制器,这是第一次接触到模型。与视图和控制器不同,模型是真正与数据打交道的工具,没有模型,网站就只是一个外壳,不能发挥真实的作用,因此它是框架中最根本的部分。现在就让我们来实现 User 模型吧。
在 models 目录中创建 user.js 的文件,内容如下:
models/user.js
var mongodb = require('./db'); function User(user) { this.name = user.name; this.password = user.password; }; module.exports = User; User.prototype.save = function save(callback) { // 存入 Mongodb 的文档 var user = { name: this.name, password: this.password, }; mongodb.open(function(err, db) { if (err) { return callback(err); } // 读取 users 集合 db.collection('users', function(err, collection) { if (err) { mongodb.close(); return callback(err); } // 为 name 属性添加索引 collection.ensureIndex('name', { unique: true }); // 写入 user 文档 collection.insert(user, { safe: true }, function(err, user) { mongodb.close(); callback(err, user); }); }); }); }; User.get = function get(username, callback) { mongodb.open(function(err, db) { if (err) { return callback(err); } // 读取 users 集合 db.collection('users', function(err, collection) { if (err) { mongodb.close(); return callback(err); } // 查找 name 属性为 username 的文档 collection.findOne({ name: username }, function(err, doc) { mongodb.close(); if (doc) { // 封装文档为 User 对象 var user = new User(doc); callback(err, user); } else { callback(err, null); } }); }); }); }; 复制代码
以上代码实现了两个接口,User.prototype.save 和 User.get,前者是对象实例的方法,用于将用户对象的数据保存到数据库中,后者是对象构造函数的方法,用于从数据库中查找指定的用户。
视图交互
现在几乎已经万事俱备,只差视图的支持了。为了实现不同登录状态下页面呈现不同内容的功能,我们需要创建动态视图助手,通过它我们才能在视图中访问会话中的用户数据,同时为了显示错误和成功的信息,也要在动态视图助手中增加响应的函数。
在书中,在 app.js 中添加的视图交互代码是:
app.dynamicHelpers({ user: function(req, res) { return req.session.user; }, error: function(req, res) { var err = req.flash('error'); if (err.length) return err; else return null; }, success: function(req, res) { var succ = req.flash('success'); if (succ.length) return succ; else return null; }, }); 复制代码
但是在 Express 4.x 中会报错“app.dynamicHelpers is not a function ”,此处应该添加:
app.js
app.use(flash()); app.use(function(req, res, next) { res.locals.user = req.session.user; res.locals.post = req.session.post; var error = req.flash('error'); res.locals.error = error.length ? error : null; var success = req.flash('success'); res.locals.success = success.length ? success : null; next(); }); 复制代码
注意,这段代码不要放的太靠后,应该放到路由控制代码之前。
接下来修改公共导航部分。
header.ejs
<nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">NodeJS Blog</a> </div> <div id="navbar" class="navbar-collapse collapse"> <form class="navbar-form navbar-right"> <% if (!user) { %> <a href="/login" type="submit" class="btn btn-success">登录</a> <a href="/reg" type="submit" class="btn btn-success">注册</a> <% } else { %> <a href="" type="submit" class="btn">注销</a> <% } %> </form> </div> </div> </nav> 复制代码
然后打开注册页,输入用户名、密码,点击注册按钮,发现又遇到坑了...“db.collection is not a function”。
引起这个错误的原因是你通过 npm 安装的 mongodb 的版本和你 Node.js 操作数据的 api 版本不一致,查看了一下 package.json,发现 mongodb 的版本是 3.1.13。
解决方法,下载低版本 mongodb,例:
"mongodb": "^2.2.33", 复制代码
npm install 安装。
未完待续......
以上所述就是小编给大家介绍的《12_Node.js Web 开发_博客网站》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 利用云开发优化博客小程序(二)——评论功能
- beego开发轻博客——第十讲 点赞功能
- 基于Laravel 5.4 开发的博客系统 —— MyPersimmon
- 利用云开发优化博客小程序(三)——生成海报功能
- 利用云开发优化博客小程序(一)——浏览量统计
- 独立开发变现周刊(第 1 期): Notion 也能做博客?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Definitive Guide to HTML5 WebSocket
Vanessa Wang、Frank Salim、Peter Moskovits / Apress / 2013-3 / USD 26.30
The browser is, hands down, the most popular and ubiquitous deployment platform available to us today: virtually every computer, smartphone, tablet, and just about every other form factor imaginable c......一起来看看 《The Definitive Guide to HTML5 WebSocket》 这本书的介绍吧!