Skip to content

Latest commit

 

History

History
244 lines (177 loc) · 7.75 KB

4.4 功能设计.md

File metadata and controls

244 lines (177 loc) · 7.75 KB

4.4.1 功能与路由设计

在开发项目之前,我们首先需要明确电商网站要实现哪些功能。由于本教程面向初学者,所以只实现了电商最基本的功能,其余的功能读者可自行实现。

功能及路由设计如下: require('./login')(app); require('./home')(app); require('./logout')(app); require('./register')(app); require('./cart')(app);

  1. 注册
    1. 注册页:GET /register
    2. 注册(包含上传头像):POST /register
  2. 登录
    1. 登录页:GET /login
    2. 登录:POST /login
  3. 登出:GET /logout
  4. 商品
    1. 查看商品页:GET /home
    2. 添加商品页:GET /addcommodity
    3. 添加商品:POST /addcommodity
  5. 购物车
    1. 购物车商品页:GET /cart
    2. 添加购物车商品页:GET /addToCart/:id
    3. 删除购物车商品: GET /removeToCart/:id
    4. 购物车结算:GET /cart/clearing

由于我们博客页面是后端渲染的,所以只通过简单的 <a>(GET)<form>(jQuery) 与后端进行交互,如果使用其他前端框架(如 angular、vue、react 等等)可通过 Ajax 与后端交互,则 api 的设计应尽量遵循 restful 风格。

restful

restful 是一种 api 的设计风格,提出了一组 api 的设计原则和约束条件。

如上面删除购物车商品的路由设计:

GET /removeToCart/:id

restful 风格的设计:

DELETE /removeToCart/:id

可以看出,restful 风格的 api 更直观且优雅。

更多阅读:

  1. http://www.ruanyifeng.com/blog/2011/09/restful
  2. http://www.ruanyifeng.com/blog/2014/05/restful_api.html
  3. http://developer.51cto.com/art/200908/141825.htm
  4. http://blog.jobbole.com/41233/

4.4.2 会话

由于 HTTP 协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识别具体的用户,这个机制就是会话(Session)。关于 Session 的讲解网上有许多资料,这里不再赘述。参考:

  1. http://justsee.iteye.com/blog/1570652
  2. https://www.zhihu.com/question/19786827

cookie 与 session 的区别

  1. cookie 存储在浏览器(有大小限制),session 存储在服务端(没有大小限制)
  2. 通常 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

4.4.3 页面通知

我们还需要这样一个功能:当我们操作成功时需要显示一个成功的通知,如登录成功跳转到主页;当我们操作失败时需要显示一个失败的通知,如注册时用户名被占用了,需要显示一个 用户名已存在! 的通知。

4.4.4 权限控制

电商购物网站,我们没有登录的话只能浏览商品,登陆后才能查看购物车、购买商品,,这就是权限控制。我们也来给博客添加权限控制,如何实现页面的权限控制呢?我们可以把用户状态的检查封装成一个中间件,在每个需要权限控制的路由加载该中间件,即可实现页面的权限控制。在 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();
    }
};

可以看出:

  1. checkLogin: 当用户信息(req.session.user)不存在,即认为用户没有登录,则跳转到登录页,同时显示 请先登录 的通知,用于需要用户登录才能操作的页面及接口
  2. checkNotLogin: 当用户信息(req.session.user)存在,即认为用户已经登录,则跳转到之前的页面,同时显示 已登录 的通知,如登录、注册页面及登录、注册的接口

4.4.5 接口介绍

最终我们创建以下路由文件:

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 启动博客,访问以下地址查看效果:

  1. http://localhost:3000/login
  2. http://localhost:3000/home
  3. http://localhost:3000/cart
  4. http://localhost:3000/register

上一节:4.3 配置文件

下一节:4.5 页面设计