Node.jsを実際に最初から最後まで学習します。 パート1

背景



Node.jsモジュールとあらゆる種類の既製のソリューションにはさまざまなドキュメントがありますが、サイトの作成を開始すると、「どこから始めますか?」という問題が発生します。 Node.jsを実際に学習した私の経験をお伝えしたいと思います。 タスクは非常にシンプルで簡単です-トランスミッターを地図上に表示したり、旅行ルートを描画したり、ファンタジーにかかる時間など、インターネットサービスを備えたGPSトラッカー。 このプロジェクトは商業的ではなく、人類のために書かれています。



職場に装備します



私は職場と自宅の両方で仕事をしなければなりません。仕事は決してサイト構築とは関係がなく、これがワークフローに干渉することはありません。 したがって、IDEを選択すると、その選択はCloud9 IDEで決まりました 。 便宜上、 WebStormは自宅使用されました。 すべてのサイトデータはどこかに保存する必要があります。 理論的な部分を研究した後、実際にはこのタイプのDBMSに精通することが決定されました。 職場に執着しないように、 MongoDBmongohq.comのベースの無料ホスティングがDBMSとして選択されました。

したがって、空のプロジェクトと空のベースがあります。 続行できます。

1. Webアプリケーションフレームワーク、最も一般的なのはエクスプレスです。

2.このサイトは完全にHTML5で記述することに決定したため、テンプレートエンジンとしてEJSが選択されました。

3. MongoDBドライバーには多数ありますが、私の選択はmongodbでした

4.ユーザー入力の検証、 node-validator



サイト構造



 BigBrother-\
       -コントローラー(データベースを操作するためのすべてのオブジェクト)
       -public-\(静的にファイル(css、js、images))
             -css
             -js
             -画像
       -ルーター(サイトルート)
       -ビュー(ページテンプレート)
       -config.js(データベースへの接続などの設定)
       -server.js(サーバー自体)


最初のステップ:ログイン



トピックは学習資料として使用され、タスクは典型的なもので、ユーザーが登録、パスワードの回復、サイトへのログインの記憶、メールの監視、単独でのログインを行えるようにしました。



1.サイトの顔


アイデア:画面の隅にあるボタンをクリックすると、入力されたデータの部分的なチェックでサイトを登録/入力する機能を備えたモーダルウィンドウが表示されます(将来、チェックが複雑になる可能性があります)。

スタイルシートstyle.css
@import url(http://fonts.googleapis.com/css?family=Tenor+Sans&subset=latin,cyrillic); body{ font-family: 'Tenor Sans', sans-serif; } #mainmap{ width : 100%; } #topmenu{ width: 100%; height: 80px; background-color: white; } #topmenu #user{ background-color: rgb(228, 228, 228); cursor: pointer; position: absolute; top: 20px; right: 20px; vertical-align: middle; text-align: center; padding: 10px; border: 1px solid gray; } #topmenu #user:hover{ background-color: rgb(188, 188, 188); border: 1px solid gray; } .window{ position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background-color: rgba(40,40,40,0.5); z-index: 9999; color: rgb(80,80,80); display: none; } .window .back{ position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; z-index: 0; } .window .wrap{ position: fixed; width: 500px; height: 400px; top: 50%; left: 50%; margin: -200px -250px; background-color: white; border: 1px solid silver; padding: 10px; z-index: 1; } .window .wrap .header{ font-size: 25px; color: rgb(40,40,40); width: 100%; border-bottom: 1px solid gray; padding-bottom: 5px; } .window .wrap .header .active{ color: rgb(40,90,40); background-color: rgb(220,200,200); } .window .wrap .header div{ display: inline; cursor: pointer; background-color: rgb(230,240,240); padding: 2px; } .window .wrap .header div:hover{ color: rgb(40,40,90); background-color: rgb(230,230,230); } .window .wrap .msg{ display: none; position: absolute; top: 41px; left: 10px; width: 480px; background-color: rgb(220,100,100); padding: 10px; color: black; } .window .wrap .line{ margin-top: 10px; margin-left: 50px; } .window .wrap .line .label{ font-size: 20px; } .window .wrap .line .edit input[type='text'], .window .wrap .line .edit input[type='email'], .window .wrap .line .edit input[type='password']{ width: 400px; height: 25px; margin-top: 5px; border: 1px solid silver; font-size: 20px; } .window .wrap .line .edit input[type='text'], .window .wrap .line .edit input[type='email'], .window .wrap .line .edit input[type='password']:focus{ border: 1px solid gray; } .window .wrap .line .edit .error{ border-color: red; } .window .wrap .buttons{ position: absolute; width: 100%; height: 40px; left: 0; bottom: 0; background-color: rgb(240,240,240); color: rgb(40,40,40); } .window .wrap .buttons .button{ float: right; padding: 5px; border: 1px solid gray; margin: 5px; cursor: pointer; } .window .wrap .buttons .button:hover{ background-color: rgb(188, 188, 188); border: 1px solid gray; }
      
      







Index.ejsホーム
  <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" > <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" > <!-- CSS !--> <link rel="stylesheet" href="css/reset.css"> <link rel="stylesheet" href="css/style.css"> <!-- Utils !--> <script src="http://yandex.st/jquery/1.8.2/jquery.min.js"></script> <script src="js/jquery.cookie.js"></script> <script src="js/core.js"></script> <title>BigBrother - <%= title %></title> </head> <body> <div id="topmenu"> <div id="user"> Login </div> </div> <div class="window" id="login"> <div class="wrap"> <div class="header"> <div id="pagelogin" class="active">Login</div> / <div id="pageregister" class="">Registration</div> </div> <div class="line" style="margin-top: 80px"> <div class="label">Email:</div> <div class="edit"><input type="email" id="email"/></div> </div> <div class="line" style="margin-top: 10px"> <div class="label">Password:</div> <div class="edit"><input type="password" id="password"/></div> </div> <div class="line" style="margin-top: 10px" id="confirmationpassworddiv"> <div class="label">Confirmation password:</div> <div class="edit"><input type="password" id="confirmationpassword"/></div> </div> <div class="line" style="margin-top: 10px"> <div class="label">Stay online:</div> <div class="edit"><input type="checkbox" id="stayonline"/></div> </div> <div class="buttons"> <div class="button" id="logincancel">cancel</div> <div class="button" id="loginsbmt">login</div> </div> <div class="msg"> </div> </div> <div class="back"></div> </div> <script type="text/javascript"> jQuery(window).load(function(){ Init(); }); </script> </body> </html>
      
      







Core.jsイベントとハンドラー
 function Init(){ /* LOGIN WINDOW */ //Init vars var user = jQuery('#user'); var loginwindow = jQuery('#login'); var loginemail = jQuery('#email'); var loginpassword = jQuery('#password'); var confirmationpassword = jQuery('#confirmationpassword'); var pagelogin = jQuery('#pagelogin'); var pageregister = jQuery('#pageregister'); var confirmationpassworddiv = jQuery('#confirmationpassworddiv'); var loginmsg = jQuery(jQuery('.msg',loginwindow)[0]); var stayonline = jQuery('#stayonline'); confirmationpassworddiv.hide(); loginmsg.hide(); //Set events confirmationpassword.keypress(inputkeypress); loginpassword.keypress(inputkeypress); loginemail.keypress(inputkeypress); user.click(function(){ loginwindow.fadeIn('fast'); }); jQuery('#logincancel').click(function(){ hidewindow(); }); jQuery('#loginsbmt').click(function(){ var isLogin = pagelogin.hasClass('active'); var error = false; var errormsg = ''; if (loginemail.val()===''){ error = true; loginemail.addClass('error'); errormsg += 'Type your email'; }else loginemail.removeClass('error'); if (!isLogin){ if (loginpassword.val()===''){ error = true; loginpassword.addClass('error'); errormsg += '<br/>Type your password'; }else loginpassword.removeClass('error'); if (confirmationpassword.val()===''){ error = true; confirmationpassword.addClass('error'); errormsg += '<br/>Type your confirmation password'; }else confirmationpassword.removeClass('error'); if (confirmationpassword.val()!=loginpassword.val()){ error = true; confirmationpassword.addClass('error'); errormsg += '<br/>Password not same'; } } if (!error) { if (!isLogin) registeruser(); else loginuser(); }else{ loginmsg.html(errormsg); loginmsg.show(); } }); jQuery('.back',loginwindow).click(function(){ hidewindow(); }); pagelogin.click(function(){ pagelogin.addClass('active'); pageregister.removeClass('active'); confirmationpassworddiv.hide(); loginmsg.hide(); jQuery('#loginsbmt').html('login'); }); pageregister.click(function(){ pagelogin.removeClass('active'); pageregister.addClass('active'); confirmationpassworddiv.show(); loginmsg.hide(); jQuery('#loginsbmt').html('register'); }); //Check login state checklogin(); //Other function function inputkeypress(){ if (jQuery(this).val()!=='') jQuery(this).removeClass('error'); } //Hide login window and clear state function hidewindow(){ loginwindow.fadeOut('fast',function(){ loginmsg.hide(); confirmationpassword.removeClass('error'); loginpassword.removeClass('error'); loginemail.removeClass('error'); pagelogin.click(); }); } //Do login function loginuser(){ jQuery.ajax({ type: "POST", url: "/auth", data: { email: loginemail.val(), password: loginpassword.val(), stayonline: stayonline.val()==='1'} }).done(function( msg ) { loginpassword.val(''); if (msg.error){ loginmsg.html(msg.msg); loginmsg.show(); }else{ jQuery.cookie('sessionid',msg.sessionid); loginmsg.html(msg.msg); loginmsg.show(); setTimeout(function() { hidewindow(); checklogin(); }, 1000); } }); } //Check login state function checklogin(){ user.html('loading...'); jQuery.ajax({ type: "GET", url: "/auth" }).done(function( msg ) { if (msg.error) { user.html('login'); user.unbind('click'); user.click(function(){loginwindow.fadeIn('fast');}); } else { user.html(msg.displayname); user.unbind('click'); user.click(logout); } }); } //Do log out function logout(){ jQuery.ajax({ type: "DELETE", url: "/auth" }).done(function(msg){ if (msg.error){ alert(msg.msg); return; } user.html('login'); user.unbind('click'); user.click(function(){ loginwindow.fadeIn('fast'); }); }); } //Register new user function registeruser(){ jQuery.ajax({ type: "POST", url: "/auth/register", data: { email: loginemail.val(), password: loginpassword.val(), confirmationpassword: confirmationpassword.val(), stayonline: stayonline.val()==='1'} }).done(function( msg ) { loginpassword.val(''); confirmationpassword.val(''); if (msg.error){ loginmsg.html(msg.msg); loginmsg.show(); }else{ loginmsg.html(msg.msg); loginmsg.show(); setTimeout(function() { hidewindow(); checklogin(); }, 1000); } }); } /* LOGIN WINDOW */ };
      
      









2.サーバー部分


一日の始まり、データベースコントローラーを作成します。 そのタスクはデータベースに接続することであり、さらに、他の頻繁に使用されるいくつかのプロシージャ/controllers/db.jsがそこに保存されます。

 exports.opendb = function(settings, callback){ var mongo = require('mongodb'), Server = mongo.Server, Db = mongo.Db; var server = new Server(settings.host, settings.port, {auto_reconnect: settings.auto_reconnect}); var db = new Db(settings.db, server); db.open(function(err, db) { if(!err) { db.authenticate(settings.username, settings.password, function(){callback(false, db);}); } else callback(true, db); }); }; exports.criptpassword = function(string){ var crypto = require('crypto'); return crypto.createHash('md5').update(string+global.saldo).digest("hex"); };
      
      





作業中に大量の変数を生成しないようにするには、controllersフォルダーにindex.jsファイルを作成します。 この場合、次のように書くと

 global.controllers = require('./controllers');
      
      





index.jsは変数で接続されます:

 exports.db = require('./db'); exports.users = require('./users'); exports.stayonlinesessions = require('./stayonlinesessions');
      
      





次のステップは、スタートページ/routers/index.jsのルーターを作成することです

 exports.index = function(req, res){ res.render('index',{title: 'Home'}); }; exports.auth = require('./auth');
      
      





データベースにユーザーセッションを保存します。このためにconnect-mongoパッケージを使用します。

そして実際には、 server.jsです。

グローバルモジュールと変数を初期化する

 //Modules var express = require("express"); var app = express(); var MongoStore = require('connect-mongo')(express); var dbsettings = require('./config').settings; //Server configuration global.saldo = 'fewfwef352tFRWEQF'; global.controllers = require('./controllers'); //Controllers var routers = require('./routers'); //Routers var viewEngine = 'ejs';
      
      





エクスプレスサーバーを構成する

 // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', viewEngine); app.use(express.cookieParser()); app.use(express.session({ secret: 'fegwegwe', store: new MongoStore(dbsettings), cookie: { path: '/', httpOnly: true, maxAge: 1000*60*60*24 } })); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); });
      
      





私が遭遇した小さな余談と複雑さがあります。 ルートを決定した後にセッションの初期化を指定すると、何らかの理由でセッションが機能しなくなります。そのため、セッションを以前に初期化します。

そうだね


 // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', viewEngine); /*---------------------------------------------------*/ app.use(express.cookieParser()); app.use(express.session({ secret: 'fegwegwe', store: new MongoStore(dbsettings), cookie: { path: '/', httpOnly: true, maxAge: 1000*60*60*24 } })); /*---------------------------------------------------*/ app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); });
      
      







間違った


 // Configuration app.configure(function(){ app.set('views', __dirname + '/views'); app.set('view engine', viewEngine); app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(app.router); app.use(express.static(__dirname + '/public')); /*---------------------------------------------------*/ app.use(express.cookieParser()); app.use(express.session({ secret: 'fegwegwe', store: new MongoStore(dbsettings), cookie: { path: '/', httpOnly: true, maxAge: 1000*60*60*24 } })); /*---------------------------------------------------*/ });
      
      





ルートを設定します

 app.get('/',routers.index); //  app.post('/auth/register',routers.auth.register); //  app.post('/auth',routers.auth.login); //   app.get('/auth',routers.auth.getlogin); //  :    app.del('/auth',routers.auth.logout); //  
      
      





データベースに接続し、成功したらサーバーを起動します

 //Connect to db and start global.controllers.db.opendb(dbsettings, function(error,db){ if (!error){ global.db = db; app.listen(process.env.PORT); } else console.log('Error connect to db'); });
      
      





私たちは始めようとし、メインページを見て、うまくいきます。

次に、サイトのユーザーを担当するコントローラー/controllers/users.jsについて説明します。

ユーザー登録:

 exports.register = function(email, password, callback){ global.db.collection('users', function(err, collection) { if (err){ callback(true); return; } collection.findOne({email: email.toLowerCase()}, function(eror, item){ if (item === null){ collection.insert({email: email.toLowerCase(), password: global.controllers.db.criptpassword(password), emailchack: true, roles: []},{safe:true},function(error, result){ if (err){ callback(true); return; } callback(false, result[0]); }); }else callback(true, null, 'User already exists'); }); }); };
      
      





IDでユーザーを取得する

 exports.getuser = function(id, callback){ global.db.collection('users', function(err, collection) { if (err){ callback(true); return; } var ObjectID = require('mongodb').ObjectID; if (typeof id === 'string') id = new ObjectID(id); collection.findOne({_id: id}, function(eror, item){ if (err){ callback(true); return; } callback(false, item); }); }); };
      
      





ユーザーにログインできるかどうかを確認します

 exports.checkuser = function(email, password, callback){ global.db.collection('users', function(err, collection) { if (err){ callback(true); return; } collection.findOne({email: email.toLowerCase(), password: global.controllers.db.criptpassword(password)}, function(error,item){ if (item === null) item = {}; callback(error, item.emailchack, item); }); }); };
      
      





ユーザーが「remember me」チェックボックスをオンにすると、ハッシュされたユーザーIDがCookieに追加され、「stayonlinesessions」ドキュメントコントローラーにすべてが保存されます。 セッションを担当するコントローラーを作成しましょう/controllers/stayonlinesessions.js

  exports.savesession = function(user_id, callback){ global.db.collection('stayonlinesessions', function(err, collection) { if (err){ callback(true); return; } var hash = global.controllers.db.criptpassword(user_id.toString()); collection.insert({user_id: user_id, hash: hash, createdate: new Date()},{safe:true},function(error, result){ if (err){ callback(true); return; } callback(false, hash); }); }); }; exports.getsession = function(hash, callback){ global.db.collection('stayonlinesessions', function(err, collection) { if (err){ callback(true); return; } collection.findOne({hash: hash}, function(error,item){ if (err || item === null){ callback(true); return; } global.controllers.users.getuser(item.user_id, function(error, user){ if (err || user === null){ callback(true); return; } callback(false, user); }); }); }); }; exports.delsession = function(hash, callback){ global.db.collection('stayonlinesessions', function(err, collection) { if (err){ callback(true); return; } collection.remove({hash: hash}, {safe: true}, function(err,removed){ if (err || !removed){ callback(true); return; } callback(false); }); }); };
      
      





そのため、登録とセッションを担当するコントローラーがあります。ここで、承認ルートを実装する必要があります。 これらの関数/routers/auth.jsを担当します。

ユーザー登録

 exports.register = function(req, res){ var email = req.body.email; var password = req.body.password; var confirmationpassword = req.body.confirmationpassword; var stayonline = req.body.stayonline; //   var check = require('validator').check; if (!check(email).len(6, 64).isEmail() || !check(password).notNull() || !check(password).equals(confirmationpassword)){ res.send({ error: true, msg: 'Check your' }); return; } //   global.controllers.users.register(email, password, function(error, user, msg){ if (error){ if (typeof msg == 'undefined' || msg===null ) msg = 'Register error'; res.send({ error: true, msg: msg }); return; } //  req.session.authorized = true; req.session.user_id = user._id; req.session.username = user.email; //   " "        if (stayonline){ global.controllers.stayonlinesessions.savesession(user._id, function(error, hash){ res.send({ error: false, msg: 'Success register email: '+email, sessionid: hash }); }); }else{ res.send({ error: false, msg: 'Success register email: '+email }); } }); };
      
      





サイトにログイン

 exports.login = function(req, res){ var email = req.body.email; var password = req.body.password; var stayonline = req.body.stayonline; global.controllers.users.checkuser(email, password, function(error, canlogin, user){ if (error || !canlogin){ res.send({ error: true, msg: 'Check your email or password' }); return; } req.session.authorized = true; req.session.user_id = user._id; req.session.username = user.email; if (stayonline){ global.controllers.stayonlinesessions.savesession(user._id, function(error, hash){ res.send({ error: false, msg: 'Success login email: '+user.email, sessionid: hash }); }); } else { res.send({ error: false, msg: 'Success login email: '+user.email }); } }); };
      
      





ログアウト

 exports.logout = function(req, res){ if (!req.session.authorized){ res.send({ error: true, msg: 'You are not loggined' }); return; } req.session.authorized = false; delete req.session.username; delete req.session.user_id; //     ,    if (typeof req.cookies.sessionid !== 'undefined' && req.cookies.sessionid !== ''){ global.controllers.stayonlinesessions.delsession(req.cookies.sessionid, function(error){ if (error){ console.log('Session was not deleted'); return; } }); } res.send({ error: false }); };
      
      





現在の認証ステータスを取得する

 exports.getlogin = function(req, res){ if (!req.session.authorized){ //   ,         global.controllers.stayonlinesessions.getsession(req.cookies.sessionid, function(error, user){ if (error){ res.send({ error: true, msg: 'You are not loggined' }); return; } req.session.authorized = true; req.session.user_id = user._id; req.session.username = user.email; res.send({ error: false, displayname: req.session.username }); }); }else{ res.send({ error: false, displayname: req.session.username }); } };
      
      





クッキーに関するいくつかのポイント。 セッションモジュールの初期化中に保存時間を指定しない場合、ブラウザに保存されません。 これは、1つの簡単なセットアップで処理されます。

 app.use(express.session({ secret: 'fegwegwe', store: new MongoStore(dbsettings), cookie: { path: '/', httpOnly: true, maxAge: 1000*60*60*24 } // maxAge -     }));
      
      





クライアント側では、Cookieの操作は非常に簡単です。 jquery.cookie.jsプラグインを使用しました

 jQuery.cookie('sessionid',msg.sessionid);
      
      





その結果、承認、登録、「記憶」機能を受け取ったので、メールアドレスの確認、パスワードの回復を記載したメールを送信する必要がありますが、これは次回です。



All Articles