本篇文章主要參考自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

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

步驟

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

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

$ express -e todo
create : todo
create : todo/package.json
create : todo/app.js
create : todo/public
create : todo/public/javascripts
create : todo/public/images
create : todo/public/stylesheets
create : todo/public/stylesheets/style.css
create : todo/routes
create : todo/routes/index.js
create : todo/routes/users.js
create : todo/views
create : todo/views/index.ejs
create : todo/views/error.ejs
create : todo/bin
create : todo/bin/www install dependencies:
$ cd todo && npm install run the app:
$ DEBUG=todo:* ./bin/www
ejs-locals以及mongoose加入dependencies

package.json

{
"name": "todo",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.12.0",
"cookie-parser": "~1.3.4",
"debug": "~2.1.1",
"ejs": "~2.3.1",
"express": "~4.12.2",
"morgan": "~1.5.1",
"serve-favicon": "~2.2.0",
"ejs-locals" : "1.0.2",
"mongoose" : "3.8.17",
"uid-safe" : "1.1.0"
}
}
安裝dependencies
$ cd todo && npm install
Hello Wolrd!

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

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

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

todo
|-- node_modules
| |-- ejs
| |-- ejs-locals
| |-- express
| `-- mongoose
|
|-- public
| |-- images
| |-- javascripts
| `-- stylesheets
| |-- style.css
|
|-- routes
| `-- index.js
|
|-- views
| |-- index.ejs
| `-- layout.ejs
|
|-- .gitignore
|
|-- app.js
|
`-- package.json
  • node_modules
    • 包含所有project相關套件
  • public
    • 包含所有靜態檔案
  • routes
    • 所有動作及商業邏輯
  • views
    • 包含action views, partials及layout
  • app.js
    • 包含設定、middlewares以及routes的分配
  • package.json
    • 相關套件的設定檔
MongoDB以及Mongoose設定

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

$ mongod

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

db.js

var mongoose = require('mongoose');
var Schema = mongoose.Schema; var Todo = new Schema({
user_id : String,
content : String,
updated_at : Date
}); mongoose.model('Todo', Todo);
mongoose.connect('mongodb://localhost/express-todo');

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

app.js

require('./db');

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

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

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 routes = require('./routes/index'); var app = express();
var engine = require('/ejs-locals'); // view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.engine('ejs', engine); // uncomment after placing your favicon in /public
//app.use(favicon(__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('/', routes); // 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 handlers // development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
} // production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
}); module.exports = app;
修改project title

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;

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

var express = require('express');

exports.index = function(req, res) {
res.render('index', {title : 'Express Todo Example'});
};
修改index view

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

views/index.ejs

<% layout( 'layout' ) -%>

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

routes/index.js

首先先require mongooseTodo model

var mongoose = require('mongoose');
var Todo = mongoose.model('Todo'); //新增成功後將頁面導回首頁.
exports.create = function(req, res) {
new Todo({
content : req.body.content,
updated_at : Date.now()
}).save(function(err, todo, count) {
res.redirect( '/' );
});
};

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

app.js

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

routes/index.js

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

views/index.ejs

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

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

routes/index.js

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

views/index.ejs

// 在迴圈裡加一個删除連結
<% todos.forEach(function(todo) { %>
<p>
<span>
<%= todo.content %>
</span>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</span>
</p>
<% }); %>

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

app.js

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

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

routes/index.js

exports.edit = function(req, res) {
Todo.find(function(err, todos) {
res.render('edit', {
title : 'Express Todo Example',
todos : todos,
current : req.params.id
});
});
};

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

view/edit.js

<% layout( 'layout' ) -%>

<h1><%= title %></h1>
<form action="/create" method="post" accept-charset="utf-8">
<input type="text" name="content" />
</form> <% todos.forEach(function(todo) { %>
<p>
<span>
<% if ( todo._id == current ) { %>
<form action="/update/<%= todo._id %>" method="POST" accept-charset="utf-8">
<input type="text" name="content" value="<%= todo.content %>" />
</form>
<% } else { %>
<a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
<% } %>
</code>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</code>
</p>
<% }); %>

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

views/index.ejs

<% layout( 'layout' ) -%>

<h1><%= title %></h1>
<form action="/create" method="post" accept-charset="utf-8">
<input type="text" name="content" />
</form> <% todos.forEach(function(todo) { %>
<p>
<span>
<a href="/edit/<%= todo._id %>" title="Update this todo item"><%= todo.content %></a>
</code>
<span>
<a href="/destroy/<%= todo._id %>" title="Delete this todo item">Delete</a>
</code>
</p>
<% }); %>

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

app.js

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

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

routes/index.js

exports.update = function(req, res) {
Todo.findById(req.params.id, function(err, todo) {
todo.content = req.body.content;
todo.updated_at = Date.now();
todo.save(function(err, todo, count) {
res.redirect( '/' );
});
});
};

將更新的動作新增到routes

app.js

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

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

routes/index.js

exports.index = function(req, res) {
Todo.
find().
sort('-updated_at').
exec(function(err, todos) {
res.render('index', {
title : 'Express Todo Example',
todos : todos
});
});
}; exports.edit = function(req, res) {
Todo.
find().
sort('-updated_at' ).
exec(function(err, todos) {
res.render('edit', {
title : 'Express Todo Example',
todos : todos,
current : req.params.id
});
});
};
多重使用者

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

app.js

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

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

routes/index.js

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

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

routes/index.js

... function ( req, res, next ){
// ...
}; ...( function( err, todo, count ){
if( err ) return next( err ); // ...
});
最後一步,執行
$ 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. Excel文件比较工具的使用

    本工具用于比较两个文件夹中对应Excel工作簿中单元格数据是否不同. 如果有内容不同的单元格,就在结果报告中表示出来. 点击如下链接,下载. Excel文件比较工具.rar 解压缩后,看到1个exe文 ...

  2. Spring AOP中使用args表达式访问目标方法的参数

    Spring AOP 的使用过程理解 首先,aop的使用场景介绍: 1.处理一些通用的非功能性的需求,不影响业务流程,比如说打印日志.性能统计.推送消息等: 2.aop无法拦截static.final ...

  3. 01 语言基础+高级:1-7 异常与多线程_day07 【线程池、Lambda表达式】

    day07[线程池.Lambda表达式] 主要内容 等待与唤醒案例 线程池 Lambda表达式 教学目标 -[ ] 能够理解线程通信概念-[ ] 能够理解等待唤醒机制-[ ] 能够描述Java中线程池 ...

  4. Python语言学习前提:Pycharm的使用

    一.Pycharm的使用 1.点击Pycharm的图标 2.点击首页Create New Project > 在弹出的页面点击Pure Python 3.选择项目文件存放的位置,选择完成之后点击 ...

  5. 第二代网关GateWay搭建流程

    Spring Cloud第二代网关GateWay是由纯Netty开发,底层为Reactor,WebFlux构建,不依赖任何Servlet容器,它不同于Zuul,使用的是异步IO,性能较Zuul提升1. ...

  6. mock简单的json返回

    针对非常简单的json返回串,我们也不一定非得通过freemarker模板的方式来构造返回数据,这里看实际的需求,如果返回的内容是固定的,而且json又非常简单,我们也可以直接写在程序里面,下面的接口 ...

  7. Graylog

    Graylog #Graylog 是与 ELK 可以相提并论的一款集中式日志管理方案,支持数据收集.检索.可视化 ​#Graylog 架构 - Graylog 负责接收来自各种设备和应用的日志,并为用 ...

  8. Linux基础篇八:VIM

    新知识: 普通模式光标跳转: G     ##光标跳转到末端  (shift +g) gg   ##光标跳转到开端 Ngg 15gg  ##光标跳转到当前文本中的15行 $     ##光标移动到当前 ...

  9. C# 接口练习

    #define debug using System; using System.Collections; namespace ConsoleApp1 { interface IAnimal { in ...

  10. wireshark的过滤命令

    1.ip.addr == 192.168.1.1 这种是目标地址和源地址都是 后面指定的IP