最近はWebアプリケーションでローカルのソフトウェアを置き換えてしまおうという試みが多くなっています。画像編集や表計算、ワードプロセッサなど様々にあると思いますが、HTML/JavaScript/スタイルシートだけで作成した場合の問題はデータを保存する場所がないということです。
localStrageを使う方法もありますが、容量が5MBまでの制限があったり、他の人とデータ共有できないという問題があります。そこでぜひ使ってみて欲しいのがニフティクラウド mobile backendのJavaScript SDKです。
JavaScript SDKを組み込めば、それだけでデータをサーバ(mBaaS)に保存できます。今回はその一例としてWebチャットを作ってみました。URLはDropboxのもので、データの保存に際してデータベースを用意していません。もの凄く手軽にWebアプリケーションが作れるようになります。
完成ソース
完成したソースはGitHubにアップロードしてあります。もしうまく動かない場合は参考にしてください。
動作デモ
動作しているデモはこちらで確認できます。URLはDropboxのもので、HTML/JavaScriptのみで動いているのが確認してもらえるかと思います。
用意するもの
ニフティクラウド mobile backendのアカウントを取得する
次にニフティクラウド mobile backendのユーザ登録を行います。もしアカウントを既にお持ちでしたら飛ばしてください。
初回ログイン
ユーザ登録が終わりましたら先ほどのログインボタンをクリックした画面に戻ります。
署名をサーバサイドで行う
JavaScript SDKの難点としては、初期化するタイミングでアプリケーションキーとクライアントキーを指定しなければならない点です。これは認証として用いていますので漏洩するのは良くありません。そこでHeroku上にサーバを立てて、署名だけサーバサイドで行うようにします。
require 'sinatra' require 'sinatra/reloader' require 'openssl' require 'base64' require 'json' set :root, File.dirname(__FILE__) options '/*' do response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Headers'] = 'Content-Type' end post '/sign' do response.headers['Access-Control-Allow-Origin'] = '*' content_type :json CLIENT_KEY = 'CLIENT_KEY' json = JSON.parse(request.body.read) {signature: Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), CLIENT_KEY, json["forEncodeString"])).strip()}.to_json end
ソースはこれだけです。ニフティクラウド mobile backendのクライアントキーで CLIENT_KEY を置き換えてください。
POST http://example-heroku.herokuapp.com/sign {"signature":"sG73G2Yh73BeDKJHeB8qIIRH/k+/4ELaSC0jx+nGt3I="}
に署名したい内容を送ると、署名後の文字列を返してくれます。
JavaScript SDKを修正する
署名をサーバサイドで行うのに合わせてJavaScript SDKも修正します。
署名作成を行っている NCMB._createSignature の最後を
var hash = CryptoJS.HmacSHA256(forEncodeString, _clientKey); var signature = CryptoJS.enc.Base64.stringify(hash); return signature;
から
return NCMB._ajax( null, null, "POST", "http://example-heroku.herokuapp.com/sign", {forEncodeString: forEncodeString}, "", "");
にします。example-herokuの部分は自分のHerokuアプリに合わせて修正してください。
さらに Ajax処理を行っている NCMB._ajax を修正します。まずヘッダーをニフティクラウド mobile backendの処理時のみにします。
xhr.setRequestHeader("X-NCMB-Application-Key", NCMB.applicationKey ); xhr.setRequestHeader("X-NCMB-Timestamp", timestamp); xhr.setRequestHeader("X-NCMB-Signature", signature);
を次のように修正します。
if (url.match(/https:\/\/mb\.api\.cloud\.nifty\.com/)) { xhr.setRequestHeader("X-NCMB-Application-Key", NCMB.applicationKey ); xhr.setRequestHeader("X-NCMB-Timestamp", timestamp); xhr.setRequestHeader("X-NCMB-Signature", signature); }
NCMB._ajaxの返り値でPromiseを使いたいので、
return promise._thenRunCallbacks(options);
を
return promise;
にします。
そうすると署名処理部分が以下のようにできます。
signature = NCMB._createSignature(route, className, objectId, url, method, timestamp); var data = dataObject; return NCMB._ajax(route, className, method, url, dataObject, signature, timestamp)
のようになっていたのが署名処理を非同期化しましたので、
return NCMB._createSignature(route, className, objectId, url, method, timestamp).then(function(response){ var signature = response.signature; return NCMB._ajax(route, className, method, url, dataObject, signature, timestamp).then(null, : });
Promiseを使うことで大きな変更なく実装が進められます。
チャット画面作成
HTMLファイルを作成します。今回はBootstrapを使っています。app.js、logic.jsについては後述します。全体像は次のようになります。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content=""> <title>Webチャット</title> <link href="//code.htmlhifive.com/h5.css" rel="Latest"> <!-- stylesheet compiled and minified CSS --> <link rel="stylesheet" href="bootstrap.min.css"> <link href="style.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <div class="container"> <div class="header"> <ul class="nav nav-pills pull-right"> <li class="active"><a href="#">Home</a></li> </ul> <h3 class="text-muted">Webチャット</h3> </div> <div class="row marketing"> <div class="col-lg-12"> <form role="form"> <div class="form-group"> <label for="exampleInputEmail1">名前</label> <input type="text" class="form-control" id="name" placeholder="名前を入力"> </div> <div class="form-group"> <label for="exampleInputMessage">発言</label> <input type="text" class="form-control" id="word" placeholder="何か適当に入力してくださいな"> </div> <button type="submit" class="btn btn-default submit-button">投稿</button> </form> </div> </div> <div class="messages"> </div> <div class="footer"> <p>© MOONGIFT 2014</p> </div> </div> <!-- /container --> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <script src="jquery.min.js"></script> <script src="ejs-h5mod.js"></script> <script src="h5-1.1.12.dev.js"></script> <script src="bootstrap.min.js"></script> <script src="ncmb-1.2.2.js"></script> <script src="logic.js"></script> <script src="app.js"></script> </body> </html>
初期データを取得
今回は hifive というWebアプリケーションフレームワークを使っています。詳細は省きますが、処理としては次のようになります。
app.js __ready: function() { var me; me = this; return this.chatLogic.getData().done(function(messages) { return me.view.append(".messages", 'messages_template', { messages: messages }); }).fail(function(data) { return console.log('error', data); }); },
初期設定が終わった段階で __ready メソッドが呼ばれます。その中では chatLogic の getData メソッドを実行しています。処理が成功すれば done が呼ばれますので、その中ではテンプレートを使って
の中にHTMLをレンダリングします。
chatLogic.getData メソッドは次のようになります。
MessageClass: function() { return NCMB.Object.extend("MessageClass"); }, getData: function() { var dfd, query; dfd = this.deferred(); query = new NCMB.Query(this.MessageClass()); query.descending("createDate"); query.find({ success: function(data) { return dfd.resolve(data); }, error: function(data) { return dfd.reject(data); } }); return dfd.promise(); },
処理をPromise化していますが、内容としては ニフティクラウド mobile backend のデータストアで MessageClass を呼び出して、作成日時の逆順(query.descending(“createDate”))でデータを取得しています。処理が成功すれば、Promiseのresolveを呼び出しています。
これだけでニフティクラウド mobile backendへのデータ取得部分が完了になります。
CORS(Cross-Origin Resource Sharing) について
先ほどのデータ取得部分を見ると、POSTメソッドで実行されているのが分かります。通常、外部ドメインのAjax処理はGETメソッドであればJSONPを使って回避できますが、それ以外のメソッド(POST/DELETEなど)は利用できません。
ニフティクラウド mobile backendでは CORS についてドメイン制限を外していますので、POST/DELETEメソッドが自由に使えるようになっています。
新規投稿
Webチャットなので新規投稿も行えないといけません。まずapp.js側を見ていきます。
app.js ".submit-button click": function(context) { var me, params; context.event.preventDefault(); me = this; params = { name: this.$find("#name").val(), word: this.$find("#word").val() }; return this.chatLogic.postData(params).done(function(message) { return me.view.prepend(".messages", 'messages_template', { messages: [message] }); }).fail(function(data) { return console.log('error', data); }); }
こちらも フォームに入力された名前(id=name)とメッセージ(id=word)をパラメータとして chatLogic.postData を呼び出しているだけです。次にlogic.jsを見てみます。
logic.js postData: function(params) { var dfd, message; dfd = this.deferred(); message = new (this.MessageClass()); message.set(params); message.save({ success: function(message) { return dfd.resolve(message); }, error: function(message, error) { return dfd.reject(error); } }); return dfd.promise(); }
こちらは実際にニフティクラウド mobile backendへの保存処理を行っています。といっても与えられたパラメータ(名前とメッセージ)をセットして、保存処理(saveメソッド)を呼び出しているだけです。これだけでデータは永続的にクラウド上に保存されます。
完全に個人用途であればJavaScript SDKをそのまま使うこともできるかと思います。外部公開であれば今回のように署名処理をサーバサイドで行い、利用できるドメインを制限するのが良いでしょう。
何よりHTML/JavaScriptだけでサーバサイドにデータを保存し、共有できる仕組みが簡単に構築できてしまうのが魅力ではないでしょうか。データストアはデータベースのように、さらにファイルストアを使えば写真やバイナリファイルをアップロードすることもできます。フロントエンドエンジニアの方はぜひニフティクラウド mobile backend を使って便利なWebアプリケーションを開発してください!