本文永久链接: https://www.askmac.cn/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