AngularJS+NodeJS+MongoDB+OAuthでWebサービスを作る

为了学习JavaScript,这里是关于制作Web服务的方法总结。
我们将创建一个简单的Todo列表。

AngularJS+NodeJS(ExpressJS)+MongoDB的组合

制作网络服务有许多方法可选择,但我们这次选择了上述组合。
因为它能够全部用JavaScript编写,从服务器到客户端。
据MongoDB的网站上看,这被称为MEAN堆栈。

以下的名为meanstack-sample,在实现可运行之前所做的步骤将按顺序进行说明。

    1. Yoman的设置

 

    1. 使用NodeJS创建REST API

 

    1. 安装MongoDB并与NodeJS连接

 

    1. AngularJS与REST API协同工作

 

    1. 使用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。

動かすと、以下のような画面になります。

todo.png

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
广告
将在 10 秒后关闭
bannerAds