本文永久链接: https://www.askmaclean.com/archives/try-mean.html
随着JavaScript的深入学习,在此我总结了web service的制作方法。编写了简单的Todo列表。
AngularJS+NodeJS(ExpressJS)+MongoDB
虽然搭建web service有很多方法,但这次主要讲解下述几种方法。
因为从服务器到客户端中,都可以用javascript来写。基于MonogoDB,我们称为MEAN栈。
以下我们将其取名为meanstack-sample,主要按顺序讲授直到启动为止所需要做的事。
Yeoman的设定
在制作网络应用的时候,使用yeoman的话,就可以简单地完成制作雏形以及调试,所以我很推荐大家使用。Yeoman是由Yo(项目管理)+bower(依赖性管理)+grunt(实施搭建测试)来组成的,这些都是能辅助JavaScript开发的nodejs的library。
npm install -g yo grunt-cli bower generator-angular generator-karma
Yo
使用Yo的话,就能安装各种雏形。雏形是可以在npm中搜索,通过输入
npm saerch yeoman-generator
就可以实现搜索。根据想搜索的结果,可以选择想使用的雏形比如输入
% yo webapp
或者
% yo angular
就可以生成各种雏形。
Bower
在Yo中制作雏形之后,根据需要追加JavaScript library时,通过使用bower就可以简单地对此进行管理。
安装:
bower install jquery --save
通过添加-save,就可以实现在安装的同时记录设定文件。目录中有bower.json的话,通过输入
bower install
Grunt就可以将已经记录的library之后进行汇总来一起安装。
grunt server
Grunt是构建工具。用yo制作雏形之后,输入% grunt server的话调试用的web service就会启动,连浏览器都会自行启动来确认操作。启动中的源代码会马上就被反映出来,使用起来非常方便。
另外,还有minify,这是实施发行用的相关操作,可以测试
grunt build
以及jasmin一起来执行测试。还有
grunt test
AngularJS的模板安装
让我们来试着安装AngularJS的模板吧。
输入
% mkdir angular % cd angular % yo angular
的话,就可以选择是否使用Bootstrap、Compass以及是否使用一些angular的module。全部安装之后,输入 grunt server 的话,就会出现以下样本画面
在有画面出现的情况下,更新文件的话就会马上被反映出来,所以开发起来就非常轻松。
用NodeJS(+ExpressJS)制作REST mock
首先,因为没有服务器端就无法运行,所以用Node.js准备能够返回REST近似值的项目。
安装Node.js的Web application framework。
详细内容请参考这里。
% npm install express
使用Node.js/Express作为web.js制成以下项目
var express = require('express'),
http = require('http'),
path = require('path'),
db = require('./db.js'),
application_root = __dirname;
var app = express();
app.configure(function() {
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'ejs');
// 将AngularJS的目录作为静态目录追加
app.use(express.static(path.join(application_root, "app")));
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
});
app.get('/todo', db.read); // GET的处理
app.post('/todo', db.create); // POST的处理
app.delete('/todo/:id', db.delete); // DELETE的处理
app.put('/todo/:id', db.update); // PUT的处理
// 启动服务器
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port'));
});
定下了URL,通过使用HTTP的GET、POST、DELETE、PUT,就可以实现CRUD。
通过在AngularJS端中使用HTTP method,就可以简单地对数据进行操作了。
MongoDB的安装以及与NodeJS的连接
因为已经制作了服务器端的结构,接下来将其与DB连接。
我试着用同样的JavaScript来连接MongoDB。
MongoDB的定义是(引用自维基百科)——开源的文件指向型数据库。用C++语言来实现的,由MongoDB Inc开发与支持。MongoDB不是RDBMS,被分类为所谓的NoSQL的数据库。
所以,不是使用SELECT语句等SQL,而是使用JavaScript的method来进行数据操作。
安装等准备可以在这里看见。
要从Node.js连接到MongoDB的话需要有各种各样的library。
试着去谷歌搜索下的话,会发现mongoose非常有名。
这次将使用更单纯的mongodb这个library。这几乎可以与在终端中使用MongoDB的情况相同地来使用。
% npm install mongodb
来安装,在web.js中用正在使用的db.js来制作连接部分。
var mongo = require('mongodb');
var Db = mongo.Db,
BSON = mongo.BSONPure;
var mongoUri = 'mongodb://localhost/contentdb';
// CRUD的C
exports.create = function(req, res) {
var content = JSON.parse(req.body.mydata);
Db.connect(mongoUri, function(err, db) {
db.collection('todolist', function(err, collection) {
collection.insert(content, {safe: true}, function(err, result) {
res.send();
db.close();
});
});
});
};
// CRUD的R
exports.read = function(req, res) {
Db.connect(mongoUri, function(err, db) {
db.collection('todolist', function(err, collection) {
collection.find({}).toArray(function(err, items) {
res.send(items);
db.close();
});
});
});
};
// CRUD的U
exports.update = function(req, res) {
var content = JSON.parse(req.body.mydata);
var updatedata = {};
updatedata.data = content.data;
updatedata.checked = content.checked;
var id = req.params.id;
Db.connect(mongoUri, function(err, db) {
db.collection('todolist', function(err, collection) {
collection.update({'_id':new BSON.ObjectID(id)}, updatedata, {upsert:true}, function(err, result) {
res.send();
db.close();
});
});
});
};
// CRUD的D
exports.delete = function(req, res) {
var id = req.params.id;
console.log(id);
Db.connect(mongoUri, function(err, db) { // add Db.connect
db.collection('todolist', function(err, collection) {
collection.remove({'_id':new BSON.ObjectID(id)}, {safe:true}, function(err, result) {
res.send();
db.close();
});
});
});
};
基本上是用这样的流程来处理:连接DB,选择collection,操作collection,关闭数据库。
我想也有每次不连接数据库从而保存collection的方法,但因为在heroku上的进展并不顺利,所以这次还是用这种形式。
AngularJS与REST的合作
制成就完成服务器端了,之后我们需要用Angular制作客户端。
使用yeoman作为yo angular之后的状态进行编辑。
修改app/views/main.html与app/scripts/controllers/main.js。
画面就变成了简单的TODO列表。(不知道为什么没有在HTML的中括号中表示出来,所以在此使用全角)。
<div class="hero-unit">
<input type="text" ng-model="newtodo"><input type="button" value="add todo" ng-click="createTodo()">
<ul>
<li ng-repeat="todo in todolist">
<input type="checkbox" ng-change="updateTodo(todo)" ng-model="todo.checked">
<span class="done-{{todo.checked}}">{{todo.data}}</span>
<input type="button" value="×" ng-click="deleteTodo(todo)">
</li>
</ul>
</div>
'use strict';
angular.module('angularApp').controller('MainCtrl', function ($scope, $http) {//, $templateCache) {
var url = '/todo';
$scope.todolist = [];
$scope.getTodo = function() {
$http.get(url).success(function(data) {
$scope.todolist = data;
});
};
$scope.createTodo = function() {
var todo = {};
todo.checked = false;
todo.data = this.newtodo;
var senddata = 'mydata=' + JSON.stringify(todo);
$http({
method: 'POST',
url: url,
data: senddata,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
}).success(function(response) {
$scope.getTodo();
}).error(function(response) {
});
this.newtodo = "";
};
$scope.updateTodo = function(todo) {
var senddata = 'mydata=' + JSON.stringify(todo);
$http({
method: 'PUT',
url: url + '/' + todo._id,
data: senddata,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
}).success(function(response) {
$scope.getTodo();
}).error(function(response) {
});
};
$scope.deleteTodo = function(todo) {
var senddata = 'mydata=' + JSON.stringify(todo);
console.log(senddata);
$http({
method: 'DELETE',
url: url + '/' + todo._id,
data: senddata,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
}).success(function(response) {
$scope.getTodo();
}).error(function(response) {
});
};
$scope.getTodo();
});
至此就可以运行了,我将至此的Sauce放到了github之中。
NodeJS中的OAuth认证
至此,对已经制成的项目添加认证功能。
需要修正的地方主要有以下3点。
- 使用Node.js的Passport library,追加认证功能(修正web.js)
- 在数据库的项目中追加uid
- 修正客户端的app.js,追加login.html
web.js修正
使用Node.js的Passport这个library的话,就可以简单地使其拥有各种各样的认证功能。这次参考了Passport的官方网站,追加了Twitter认证。
首先准备一个新的auth函数,确认是否通过认证,在通过认证之后可以变更为执行DB操作用。
另外consumerKey以及consumerSecret可以从Twitter的开发者页面取得。
/* passport的设定 */
var passport = require('passport'),
TwitterStrategy = require('passport-twitter').Strategy;
// Passport: Twitter 的OAuth设定
passport.use(new TwitterStrategy({
consumerKey: "your key",
consumerSecret: "your secret",
callbackURL: "/auth/twitter/callback"
}, function(token, tokenSecret, profile, done) {
// 设定用户ID
profile.uid = profile.provider + profile.id;
process.nextTick(function() {
return done(null, profile);
});
}));
// Serialized and deserialized methods when got from session
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
// Define a middleware function to be used for every secured routes
// 没有通过认证的话就返回401
var auth = function(req, res, next){
if (!req.isAuthenticated()) {
res.send(401);
} else {
next();
}
};
var app = express();
app.configure(function() {
...
// OAuth认证用
app.use(express.session({ secret: 'hogehoge', cookie: {maxAge: 1000 * 60 * 60 * 24 * 30} }));
app.use(passport.initialize()); // Add passport initialization
app.use(passport.session()); // Add passport initialization
app.use(app.router);
});
// route to log in
app.get("/auth/twitter", passport.authenticate('twitter'));
app.get("/auth/twitter/callback", passport.authenticate('twitter', {
successRedirect: '/',
falureRedirect: '/login'
}));
app.get('/todo', auth, db.read); // GET的处理。要认证(在auth函数之后执行至此的处理)
...
db.js的修正
因为每个用户都有数据,所以在MongoDB的CRUD操作时,追加uid。
...
// CRUD的C
exports.create = function(req, res) {
var uid = req.user.uid;
var content = JSON.parse(req.body.mydata);
content.uid = uid;
Db.connect(mongoUri, function(err, db) {
db.collection('todolist', function(err, collection) {
collection.insert(content, {safe: true}, function(err, result) {
res.send();
db.close();
});
});
});
};
...
app.js的修正(与追加login.html)
通过与web.js的auth函数合作,可以实现搜索到在没有通过认证的情况下,被返回的401错误的功能。
...
// 不用[]来围住的话,grunt中,进行minify之后就会报错。
$httpProvider.responseInterceptors.push(['$q', '$location',function($q, $location) {
return function(promise) {
return promise.then(function(response) {
// Success: 成功时原样返回
return response;
}, function(response) {
// Error:错误是401的话/迁移到login中。
if (response.status === 401) {
$location.url('/login');
}
return $q.reject(response);
}
);
};
}]);
...
<div class="container">
<div class="page-header">
login
</div>
<div>
<p><button class="btn-auth btn-twitter" onclick="location.href='/auth/twitter'">
Sign in with <b>Twitter</b></button></p>
</div>
</div>
总结与参考
直到追加了认证为止,源代码都会被放在github中。虽然看起来这很古老,安全性方面也会有问题。
之后使用最开始输入的BootStrap,来改善对其的成见,试着提升heroku,添加Facebook中的认证等,这能应用于许多地方。
Leave a Reply