在开发项目之前,我们首先需要明确电商网站要实现哪些功能。由于本教程面向初学者,所以只实现了电商最基本的功能,其余的功能读者可自行实现。
功能及路由设计如下: require('./login')(app); require('./home')(app); require('./logout')(app); require('./register')(app); require('./cart')(app);
- 注册
- 注册页:
GET /register
- 注册(包含上传头像):
POST /register
- 注册页:
- 登录
- 登录页:
GET /login
- 登录:
POST /login
- 登录页:
- 登出:
GET /logout
- 商品
- 查看商品页:
GET /home
- 添加商品页:
GET /addcommodity
- 添加商品:
POST /addcommodity
- 查看商品页:
- 购物车
- 购物车商品页:
GET /cart
- 添加购物车商品页:
GET /addToCart/:id
- 删除购物车商品:
GET /removeToCart/:id
- 购物车结算:
GET /cart/clearing
- 购物车商品页:
由于我们博客页面是后端渲染的,所以只通过简单的 <a>(GET)
和 <form>(jQuery)
与后端进行交互,如果使用其他前端框架(如 angular、vue、react 等等)可通过 Ajax 与后端交互,则 api 的设计应尽量遵循 restful 风格。
restful 是一种 api 的设计风格,提出了一组 api 的设计原则和约束条件。
如上面删除购物车商品的路由设计:
GET /removeToCart/:id
restful 风格的设计:
DELETE /removeToCart/:id
可以看出,restful 风格的 api 更直观且优雅。
更多阅读:
- http://www.ruanyifeng.com/blog/2011/09/restful
- http://www.ruanyifeng.com/blog/2014/05/restful_api.html
- http://developer.51cto.com/art/200908/141825.htm
- http://blog.jobbole.com/41233/
由于 HTTP 协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是会话(Session)。关于 Session 的讲解网上有许多资料,这里不再赘述。参考:
- cookie 存储在浏览器(有大小限制),session 存储在服务端(没有大小限制)
- 通常 session 的实现是基于 cookie 的,即 session id 存储于 cookie 中
我们通过引入 express-session 中间件实现对会话的支持:
app.use(session(options))
session 中间件会在 req 上添加 session 对象,即 req.session 初始值为 {}
,当我们登录后设置 req.session.user = 用户信息
,返回浏览器的头信息中会带上 set-cookie
将 session id 写到浏览器 cookie 中,那么该用户下次请求时,通过带上来的 cookie 中的 session id 我们就可以查找到该用户,并将用户信息保存到 req.session.user
。
我们还需要这样一个功能:当我们操作成功时需要显示一个成功的通知,如登录成功跳转到主页;当我们操作失败时需要显示一个失败的通知,如注册时用户名被占用了,需要显示一个 用户名已存在!
的通知。
电商购物网站,我们没有登录的话只能浏览商品,登陆后才能查看购物车、购买商品,,这就是权限控制。我们也来给博客添加权限控制,如何实现页面的权限控制呢?我们可以把用户状态的检查封装成一个中间件,在每个需要权限控制的路由加载该中间件,即可实现页面的权限控制。在 myblog 下新建 middlewares 文件夹,在该目录下新建 check.js,添加如下代码:
middlewares/check.js
module.exports = {
checkLogin: function checkLogin(req, res, next) {
if (!req.session.user) {
req.session.error = "请先登录";
res.redirect("/login");
}
next();
},
checkNotLogin: function checkNotLogin(req, res, next) {
if (req.session.user) {
req.session.error = "已登录";
return res.redirect('back');//返回之前的页面
}
next();
}
};
可以看出:
checkLogin
: 当用户信息(req.session.user
)不存在,即认为用户没有登录,则跳转到登录页,同时显示请先登录
的通知,用于需要用户登录才能操作的页面及接口checkNotLogin
: 当用户信息(req.session.user
)存在,即认为用户已经登录,则跳转到之前的页面,同时显示已登录
的通知,如登录、注册页面及登录、注册的接口
最终我们创建以下路由文件:
routes/index.js
module.exports = function(app){
require('./login')(app);
require('./home')(app);
require('./logout')(app);
require('./register')(app);
require('./cart')(app);
}
routes/cart.js
//查看购物车商品
app.get('/cart', function(req, res)
//添加购物车商品
app.get("/addToCart/:id", function(req, res)
//删除购物车商品
app.get("/delFromCart/:id", function(req, res)
//购物车结算
app.post("/cart/clearing",function(req,res)
routes/home.js
//home页面为登录时可以直接浏览
app.get('/home',function(req,res)
//添加商品页
app.get('/addcommodity',checkLogin,function(req,res)
//添加商品
app.post('/addcommodity',checkLogin, function(req,res)
routes/login.js
//登录页面
app.get('/login',function(req,res)
//登录
app.post('/login',function(req,res)
routes/logout.js
//退出登录
app.get('/logout', function(req, res)
最后, 修改 index.js 如下:
app.js
var express = require("express");
var app = express();
var path = require("path");
var mongoose = require("mongoose");
var config = require('config-lite')(__dirname);
var bodyParser = require("body-parser");
var multer = require("multer");
var session = require("express-session");
global.dbHelper = require('./common/dbHelper');
global.db = mongoose.connect(config.mongodb);
app.use(session({
secret: config.session.secret,
key: config.session.key,
cookie: {
maxAge: config.session.maxAge
}
}));
// 设定views变量,意为视图存放的目录
app.set("views", path.join(__dirname, 'views'));
// 设定view engine变量, 意为网页模板引擎
//app.set("view engine",'ejs');//ejs:可以直接嵌入变量<%= title %>
app.set("view engine", 'html');
app.engine('.html', require('ejs').__express);
// 设施bodyParser模块,是项目中可以直接引用req.body.XXXX
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(multer());
// 设定静态文件目录,比如本地文件
app.use(express.static(path.join(__dirname, 'public')));
//全局中间件,每个路由处理都会先执行这段代码
app.use(function (req, res, next) {
res.locals.user = req.session.user;
var err = req.session.error;
res.locals.message = '';
if (err) res.locals.message = '<div class="alert alert-danger" style="margin-bottom: 20px;color:red;">' + err + '</div>';
next();
});
require('./routes')(app);
app.get('/', function (req, res) {
res.render("login");
});
app.listen(config.port);
注意:中间件的加载顺序很重要。如上面设置静态文件目录的中间件应该放到 routes(app) 之前加载,这样静态文件的请求就不会落到业务逻辑的路由里。
运行 node app.js
启动博客,访问以下地址查看效果:
- http://localhost:3000/login
- http://localhost:3000/home
- http://localhost:3000/cart
- http://localhost:3000/register
上一节:4.3 配置文件
下一节:4.5 页面设计