本篇文章主要參考自DreamersLab - 用Express和MongoDB寫一個todo list。原文的教學內容是使用Express 3.x版,在這邊做簡單Express 4.12.1的todo list實作。

功能介紹

  • 用cookie來判斷使用者
  • 新增、讀取、更新、刪除待辦事項(C.R.U.D)

安裝

開發環境

確定已安裝node.js, ExpressMongoDB

  • 安裝Express 4.12.1

    1. npm install express@4.12.1
  • 我們使用Mongoose來當作我們的ORM

步驟

用Express command line tool來建立基本框架

Express預設的template engine為jade,在這範例我們採用ejs

  1. $ express -e todo
  2. create : todo
  3. create : todo/package.json
  4. create : todo/app.js
  5. create : todo/public
  6. create : todo/public/javascripts
  7. create : todo/public/images
  8. create : todo/public/stylesheets
  9. create : todo/public/stylesheets/style.css
  10. create : todo/routes
  11. create : todo/routes/index.js
  12. create : todo/routes/users.js
  13. create : todo/views
  14. create : todo/views/index.ejs
  15. create : todo/views/error.ejs
  16. create : todo/bin
  17. create : todo/bin/www
  18. install dependencies:
  19. $ cd todo && npm install
  20. run the app:
  21. $ DEBUG=todo:* ./bin/www
ejs-locals以及mongoose加入dependencies

package.json

  1. {
  2. "name": "todo",
  3. "version": "0.0.0",
  4. "private": true,
  5. "scripts": {
  6. "start": "node ./bin/www"
  7. },
  8. "dependencies": {
  9. "body-parser": "~1.12.0",
  10. "cookie-parser": "~1.3.4",
  11. "debug": "~2.1.1",
  12. "ejs": "~2.3.1",
  13. "express": "~4.12.2",
  14. "morgan": "~1.5.1",
  15. "serve-favicon": "~2.2.0",
  16. "ejs-locals" : "1.0.2",
  17. "mongoose" : "3.8.17",
  18. "uid-safe" : "1.1.0"
  19. }
  20. }
安裝dependencies
  1. $ cd todo && npm install
Hello Wolrd!

開啟Express server然後打開瀏覽器瀏覽127.0.0.1:3000就會看到歡迎頁面

  1. $ DEBUG=todo ./bin/www
檔案結構

此時專案的檔案結構大致為下:

  1. todo
  2. |-- node_modules
  3. | |-- ejs
  4. | |-- ejs-locals
  5. | |-- express
  6. | `-- mongoose
  7. |
  8. |-- public
  9. | |-- images
  10. | |-- javascripts
  11. | `-- stylesheets
  12. | |-- style.css
  13. |
  14. |-- routes
  15. | `-- index.js
  16. |
  17. |-- views
  18. | |-- index.ejs
  19. | `-- layout.ejs
  20. |
  21. |-- .gitignore
  22. |
  23. |-- app.js
  24. |
  25. `-- package.json
  • node_modules
    • 包含所有project相關套件
  • public
    • 包含所有靜態檔案
  • routes
    • 所有動作及商業邏輯
  • views
    • 包含action views, partials及layout
  • app.js
    • 包含設定、middlewares以及routes的分配
  • package.json
    • 相關套件的設定檔
MongoDB以及Mongoose設定

Ubuntu上MongoDB開機便會自動開啟,若是Mac上就輸入下面指令開啟

  1. $ mongod

並且在專案根目錄之下新增一個檔案叫做db.js來設定MongoDB和定義Schema

db.js

  1. var mongoose = require('mongoose');
  2. var Schema = mongoose.Schema;
  3. var Todo = new Schema({
  4. user_id : String,
  5. content : String,
  6. updated_at : Date
  7. });
  8. mongoose.model('Todo', Todo);
  9. mongoose.connect('mongodb://localhost/express-todo');

並且在app.js裡require剛剛新增的db.js

app.js

  1. require('./db');

並且移除掉Express預設產生,我們用不到的user route,並且加上layout support

var engine = require('/ejs-locals');
app.engine('ejs', engine);

  1. var express = require('express');
  2. var path = require('path');
  3. var favicon = require('serve-favicon');
  4. var logger = require('morgan');
  5. var cookieParser = require('cookie-parser');
  6. var bodyParser = require('body-parser');
  7. var routes = require('./routes/index');
  8. var app = express();
  9. var engine = require('/ejs-locals');
  10. // view engine setup
  11. app.set('views', path.join(__dirname, 'views'));
  12. app.set('view engine', 'ejs');
  13. app.engine('ejs', engine);
  14. // uncomment after placing your favicon in /public
  15. //app.use(favicon(__dirname + '/public/favicon.ico'));
  16. app.use(logger('dev'));
  17. app.use(bodyParser.json());
  18. app.use(bodyParser.urlencoded({ extended: false }));
  19. app.use(cookieParser());
  20. app.use(express.static(path.join(__dirname, 'public')));
  21. app.use('/', routes);
  22. // catch 404 and forward to error handler
  23. app.use(function(req, res, next) {
  24. var err = new Error('Not Found');
  25. err.status = 404;
  26. next(err);
  27. });
  28. // error handlers
  29. // development error handler
  30. // will print stacktrace
  31. if (app.get('env') === 'development') {
  32. app.use(function(err, req, res, next) {
  33. res.status(err.status || 500);
  34. res.render('error', {
  35. message: err.message,
  36. error: err
  37. });
  38. });
  39. }
  40. // production error handler
  41. // no stacktraces leaked to user
  42. app.use(function(err, req, res, next) {
  43. res.status(err.status || 500);
  44. res.render('error', {
  45. message: err.message,
  46. error: {}
  47. });
  48. });
  49. module.exports = app;
修改project title

routes/index.js

原本的預設產生的程式碼如下:

  1. var express = require('express');
  2. var router = express.Router();
  3. /* GET home page. */
  4. router.get('/', function(req, res, next) {
  5. res.render('index', { title: 'Express' });
  6. });
  7. module.exports = router;

在這個範例之中我們一律將router的分配寫在app.js當中,因此將程式碼修改為:

  1. var express = require('express');
  2. exports.index = function(req, res) {
  3. res.render('index', {title : 'Express Todo Example'});
  4. };
修改index view

在這裡我們需要一個textbox來新增待辦事項,並且用POST來傳送資料,另外別忘了設定layout

views/index.ejs

  1. <% layout( 'layout' ) -%>
  2. <h1><%= title %></h1>
  3. <form ac 大专栏  Express 4和 MongoDB打造Todo Listtion="/create" method="POST" accept-charset="utf-8">
  4. <input type="text" name="content" />
  5. </form>
新增待辦事項及存檔

routes/index.js

首先先require mongooseTodo model

  1. var mongoose = require('mongoose');
  2. var Todo = mongoose.model('Todo');
  3. //新增成功後將頁面導回首頁.
  4. exports.create = function(req, res) {
  5. new Todo({
  6. content : req.body.content,
  7. updated_at : Date.now()
  8. }).save(function(err, todo, count) {
  9. res.redirect( '/' );
  10. });
  11. };

接著在app.js當中新增對應的route

app.js

  1. // 新增下列語法到 routes
  2. app.post('/create', routes.create);
顯示待辦事項

routes/index.js

  1. // 查詢資料庫來取得所有待辦是事項.
  2. exports.index = function(req, res) {
  3. Todo.find( function(err, todos, count) {
  4. res.render('index', {
  5. title : 'Express Todo Example',
  6. todos : todos
  7. });
  8. });
  9. };

views/index.ejs

  1. // 跑迴圈顯示待辦事項
  2. <% todos.forEach(function(todo) { %>
  3. <p><%= todo.content %></p>
  4. <% }); %>
刪除待辦事項

在每一個待辦事項旁邊增加刪除的連結

routes/index.js

  1. // 根據待辦事項的id來做移除
  2. exports.destroy = function(req, res) {
  3. Todo.findById(req.params.id, function(err, todo) {
  4. todo.remove( function(err, todo) {
  5. res.redirect( '/' );
  6. });
  7. });
  8. };

views/index.ejs

  1. // 在迴圈裡加一個删除連結
  2. <% todos.forEach(function(todo) { %>
  3. <p>
  4. <span>
  5. <%= todo.content %>
  6. </span>
  7. <span>
  8. <a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
  9. </span>
  10. </p>
  11. <% }); %>

將destroy的動作新增到對應的route

app.js

  1. // 新增下列語法到 routes
  2. app.get('/destroy/:id', routes.destroy);
編輯待辦事項

當滑鼠點擊事項時,將它轉為一個text input達到編輯效果

routes/index.js

  1. exports.edit = function(req, res) {
  2. Todo.find(function(err, todos) {
  3. res.render('edit', {
  4. title : 'Express Todo Example',
  5. todos : todos,
  6. current : req.params.id
  7. });
  8. });
  9. };

新增Edit view,基本上和index view差不多,唯一不同是在被選取的待辦事項變成text input

view/edit.js

  1. <% layout( 'layout' ) -%>
  2. <h1><%= title %></h1>
  3. <form action="/create" method="post" accept-charset="utf-8">
  4. <input type="text" name="content" />
  5. </form>
  6. <% todos.forEach(function(todo) { %>
  7. <p>
  8. <span>
  9. <% if ( todo._id == current ) { %>
  10. <form action="/update/<%= todo._id %>" method="POST" accept-charset="utf-8">
  11. <input type="text" name="content" value="<%= todo.content %>" />
  12. </form>
  13. <% } else { %>
  14. <a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
  15. <% } %>
  16. </code>
  17. <span>
  18. <a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
  19. </code>
  20. </p>
  21. <% }); %>

待辦事項新增可以連到edit的link

views/index.ejs

  1. <% layout( 'layout' ) -%>
  2. <h1><%= title %></h1>
  3. <form action="/create" method="post" accept-charset="utf-8">
  4. <input type="text" name="content" />
  5. </form>
  6. <% todos.forEach(function(todo) { %>
  7. <p>
  8. <span>
  9. <a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
  10. </code>
  11. <span>
  12. <a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
  13. </code>
  14. </p>
  15. <% }); %>

將連結到編輯的route新增到app.js中

app.js

  1. app.get('/edit/:id', routes.edit);
更新待辦事項

新增update動作來更新待辦事項

routes/index.js

  1. exports.update = function(req, res) {
  2. Todo.findById(req.params.id, function(err, todo) {
  3. todo.content = req.body.content;
  4. todo.updated_at = Date.now();
  5. todo.save(function(err, todo, count) {
  6. res.redirect( '/' );
  7. });
  8. });
  9. };

將更新的動作新增到routes

app.js

  1. app.post('/update/:id', routes.update);
排序

現在待辦事項的順序為:較早創建/更新的在上面,我們要將它相反

routes/index.js

  1. exports.index = function(req, res) {
  2. Todo.
  3. find().
  4. sort('-updated_at').
  5. exec(function(err, todos) {
  6. res.render('index', {
  7. title : 'Express Todo Example',
  8. todos : todos
  9. });
  10. });
  11. };
  12. exports.edit = function(req, res) {
  13. Todo.
  14. find().
  15. sort('-updated_at' ).
  16. exec(function(err, todos) {
  17. res.render('edit', {
  18. title : 'Express Todo Example',
  19. todos : todos,
  20. current : req.params.id
  21. });
  22. });
  23. };
多重使用者

目前為止,所有使用者看到的都是同一組待辦事項,資料有可能會被外人所修改,因此,我們可利用cookie來記錄使用者資訊,讓每個人都有自己的todo list。
Express已經有內建的cookie,我們先在app.js當中新增一個middleware就好。

app.js

  1. // 將抓取使用者資訊的middleware加入app.js
  2. app.use( routes.current_user );

接著在routes/index.js增加current_user的運作邏輯

routes/index.js

  1. // 我們採用uid-safe package來替我們產生uid,別忘了要npm install uid-safe哦
  2. var uid = require('uid-safe');
  3. exports.current_user = function(req, res, next) {
  4. var user_id = req.cookies ?
  5. req.cookies.user_id : undefined;
  6. if ( ! user_id ) {
  7. uid(32).then(function(uid) {
  8. res.cookie('user_id', uid);
  9. });
  10. }
  11. next();
  12. };
Error handling

要處理錯誤我們需要新增next參數到每個action當中,一旦發生錯誤便將他傳給下一個middleware去做處理

routes/index.js

  1. ... function ( req, res, next ){
  2. // ...
  3. };
  4. ...( function( err, todo, count ){
  5. if( err ) return next( err );
  6. // ...
  7. });
最後一步,執行
  1. $ DEBUG=todo ./bin/www

用Express 4和 MongoDB打造Todo List的更多相关文章

  1. vue.js+socket.io+express+mongodb打造在线聊天

    vue.js+socket.io+express+mongodb打造在线聊天 在线地址观看 http://www.chenleiming.com github地址 https://github.com ...

  2. vue.js+socket.io+express+mongodb打造在线聊天[二]

    vue.js+socket.io+express+mongodb打造在线聊天[二] 在线地址观看 http://www.chenleiming.com github地址 https://github. ...

  3. 基于MongoDB打造.Net的分布式Session子系统

    基于MongoDB打造.Net的分布式Session子系统 Taobao有她自己的分布式session框架,.net阵营也不能落后了,在下做了个基于MongoDB的支持最多26台MongoDB的分布式 ...

  4. Express中使用mongodb存储session

    express默认有队session的支持,但是是存储在内存中的. 我们可以使用mongodb来存储会话. 但是express的各个版本中对该功能的写法是不同的. Express 2.x: app.u ...

  5. 【Node.js】二、基于Express框架 + 连接MongoDB + 写后端接口

    在上节,我们讲了如何搭建express环境,现在我们说说如何通过node.js写服务接口给前端调用 1. 首先通过MongoDB建好数据库与表格 例如,我的数据库名字为db_demo,数据库表格为go ...

  6. nodejs之使用express框架连接mongodb数据库

    var express = require('express');var router = express.Router();var app = express();var MongoClient = ...

  7. 使用Golang+Mongodb打造你的第一个站点

    很多人推荐MEAN来开发站点.MEAN就是M:mongodb,E:expressjs.A:angular最后的N:nodejs. 但是如果你亲身的体会到了js的嵌套回调的话你就会想换换别的办法了.虽然 ...

  8. NODE 基于express 框架和mongoDB的cookie和session认证 和图片的上传和删除

    源码地址 https://gitee.com/zyqwasd/mongdbSession 本项目的mongodb是本地的mongodb 开启方法可以百度一下 端口是默认的27017 页面效果 1. 注 ...

  9. 基于Express+Socket.io+MongoDB的即时聊天系统的设计与实现

    记得从高中上课时经常偷偷的和同学们使用qq进行聊天,那时候经常需要进行下载qq,但是当时又没有那么多的流量进行下载,这就是一个很尴尬的事情了,当时就多想要有一个可以进行线上聊天的网站呀,不用每次痛苦的 ...

随机推荐

  1. eclipse创建文件携带作者时间

    windows–>preference Java–>Code Style–>Code Templates code–>new Java files 编辑它 ${filecomm ...

  2. Python—守护进程管理工具(Supervisor)

    一.前言简介 1.Supervisor 是一个 Python 开发的 client/server 系统,可以管理和监控类 UNIX 操作系统上面的进程.可以很方便的用来启动.重启.关闭进程(不仅仅是 ...

  3. jupyterhub 安装配置

    安装 安装 anaconda3, 从https://www.anaconda.com/distribution/下载. 国内有代理,见这里 但不如官网新. 安装 sudo /opt/anaconda3 ...

  4. 【二进制枚举+LCS】Card Hand Sorting

    [二进制枚举+LCS]Card Hand Sorting 题目描述 When dealt cards in the card game Plump it is a good idea to start ...

  5. HDU-4553 约会安排(线段树维护连续区间)

    http://acm.hdu.edu.cn/showproblem.php?pid=4553 Problem Description 寒假来了,又到了小明和女神们约会的季节.  小明虽为屌丝级码农,但 ...

  6. sql的书写顺序

    例:select t.* from (select *  from t_user where isDelete = 1 limit 0,10) t order by t.qq select from ...

  7. redis簡單命令

  8. D. Colored Boots(STL)

    There are nn left boots and nn right boots. Each boot has a color which is denoted as a lowercase La ...

  9. RDD(八)——缓存与检查点

    RDD通过persist方法或cache方法可以将前面的计算结果缓存,默认情况下 persist() 会把数据以序列化的形式缓存在 JVM 的堆空间中. 但是并不是这两个方法被调用时立即缓存,而是触发 ...

  10. ubuntu .bashrc文件添加jdk后无法登录的解决方案

    1. 快捷键(ctl-alt-f2)进入虚拟终端 2. 执行export PATH=/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/ ...