AngularJS+NodeJS+MongoDB+OAuthでWebサービスを作る
为了学习JavaScript,这里是关于制作Web服务的方法总结。
我们将创建一个简单的Todo列表。
AngularJS+NodeJS(ExpressJS)+MongoDB的组合
制作网络服务有许多方法可选择,但我们这次选择了上述组合。
因为它能够全部用JavaScript编写,从服务器到客户端。
据MongoDB的网站上看,这被称为MEAN堆栈。
以下的名为meanstack-sample,在实现可运行之前所做的步骤将按顺序进行说明。
-
- Yoman的设置
-
- 使用NodeJS创建REST API
-
- 安装MongoDB并与NodeJS连接
-
- AngularJS与REST API协同工作
-
- 使用NodeJS进行OAuth认证
- 总结和参考资料
Yeoman的配置
请参考其他条目
使用NodeJS(+ExpressJS)创建一个类似REST的应用。
首先,由于没有服务器端,所以我们需要使用Node.js来准备一个能以类似REST方式返回值的工具。
请安装Node.js的Web应用程序框架Express。详细指南请点击这里。
% 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方法,可以轻松进行数据操作。
MongoDB的安装和与NodeJS的连接。
由于服务器端的架构已经搭建完成,接下来将与数据库进行连接。
考虑到使用相同的JavaScript语言,我选择了MongoDB。
根据维基百科,MongoDB是一个NoSQL数据库,
这是一个开源的面向文档的数据库。它用C++语言编写,由MongoDB Inc.进行开发和支持。MongoDB不是关系型数据库(RDBMS),而是属于所谓的NoSQL数据库类型。
使用JavaScript方法进行数据操作,而不使用SELECT语句等SQL。关于安装等的准备工作,可以在这里找到。
连接到MongoDB需要使用多种库。根据搜索结果,似乎mongoose是最著名的库,但这次我们将尝试使用更简单的mongodb库。它似乎可以像在终端上使用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();
});
});
});
};
基本流程是:先连接到数据库,选择集合,操作集合,最后关闭数据库。可能还有一种方式是保持连接而不必每次都连接数据库,但在尝试在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();
});
由于目前已经实现了基本的功能,我把到目前为止的源代码上传到了GitHub。
動かすと、以下のような画面になります。
NodeJSでのOAuth認証
ここまで作成したものに認証機能を付けていきます。
修正箇所は主に以下の3点です。
-
- Node.jsのPassportライブラリを利用して、認証機能追加(web.js修正)
-
- データベースの項目にuidを追加(db.js修正)
- クライアント側のapp.jsを修正、login.html追加
修复web.js
使用Node.js的Passport库,可以轻松地添加各种认证功能。这次我们将参考Passport官方网站,并添加Twitter认证。
我们将准备一个名为 “auth” 的新函数,在确认通过认证后,将更改为在认证之上执行数据库操作。
值得注意的是,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
// セッション内のUIDに紐付けてデータを作成する
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错误功能。
检查HTTP响应,如果包含401错误,则创建跳转到登录页面的处理。
AngularJS公式の$httpページを参考に作りました。
が、使う前に「$q and deferred/promise APIs」の理解が必要と記載があり、そこが理解できていないため、
過不足があるかもしれません。
...
// []で囲わないと、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认证等等,可以有很多的应用扩展。
请看以下内容:
-
- DailyJSのAngularJS回
-
- 日々之スクラッチ: Node.js + express + MongoDB on Heroku でアプリケーションを作成する
-
- チュートリアル: node.jsとHerokuでAngularJSアプリを作る Part I
-
- 【Node.js】PassportでFacebookとTwitterのOAuth認証する方法 | creator note
- SQL脳に優しいMongoDBクエリー入門 – taka512’s blog