monacaとAzure FunctionsでサーバーレスモバイルAPI連携

こんにちは、竹石です。

サーバーレスアーキテクチャといえば、AWS LambdaとAzure Functionsかと思います。

今回はAzure Functionsを使って、モバイルアプリのAPIを作る方法を紹介します。

モバイルアプリ側はmonacaです。

テーブルストレージ連携、npmパッケージの追加方法にも触れているので、参考になればと。

まずは、Azure ポータルにログインして、Function Appを作成します。

function_app_create

次にサブドメインやサブスクリプションを選びます。

ここで、一点大事なことはホスティングプランを従量課金ではなく

App Serviceプランにすることです。

従量課金プランにすると、初回コンテナ起動に時間がかかるため、

レスポンスが欲しいAPIはタイムアウトしてしまいます。

App Serviceプランにして、App Serviceをfreeにしておけばいいと思います。

function_setting

さらに、ストレージアカウントが適当な名前でつけられるので、

問題なければ、そのまま作成します。

次に、最初の関数を定義します。右ペインに目がいきますが、そのまま関数を作ると

デフォルトの名前の関数を作ってしまうので、左の「新しい関数」を押してから、

トリガーと関数名を指定して作成します。

function_first

これで、↓のようなURLが作られるので、postなりgetなりすると、レスポンスが返されます。

default_script

簡単なJSONやテキストを返すだけなら、resのbodyに値を詰めればOKです。

今回はテーブルストレージに認証情報テーブルを作成し、その値で認証するAPIにしたいと思います。

まず、ストレージを編集できるツールをダウンロードします。

Microsoft Azure Storage Explorer

認証情報は、ストレージアカウントのkeyを使用します。

storage_access

Microsoft Azure Storage Explorerのコンセントのマークから認証情報を入力し、

リフレッシュすると、ストレージにアクセスできるので、

テーブルを新しく作成します。ここではUsersというテーブルを作成しました。

create_table

初回はPartitionKeyとRowKeyカラムのみなので、Add Propertyボタンを押して、

カラムを追加して、レコード追加します。

add_record

これでストレージの準備はできたので、Functionsでストレージの情報を使いたいと思います。

スクリーンショット 2017-01-30 11.02.38

左の統合を選択し、「新しい入力」でAzure Table Storageを選択します。

テーブル名を入力し、保存します。

あとは、リクエストで受け取った値とテーブルの情報が合うか判定します。

context.bindings.Usersでテーブルの情報が取れるので、postのbodyでjsonを受け取り、

該当するログインID、パスワードがあれば、ユーザー情報を返却するようにしています。

module.exports = function (context, req) {
    var body = JSON.parse(req.body);

    if (!body || !body.loginId || !body.password) {
        context.done(null, {
            status: 400,
            body: { message: "parameter invalid." },
            headers: { 'Content-Type': 'application/json' }
        });
        return;
    }

    var info = null;
    var i = context.bindings.Users.length - 1;
    for (; i >= 0; i--) {
        if (body.loginId === context.bindings.Users[i].UserId
            && body.password === context.bindings.Users[i].Password) {
            info = context.bindings.Users[i];
            break;
        }
    }

    if (info == null) {
        context.done(null, {
            status: 401,
            body: { message: "LoginId or Password invalid."},
            headers: { 'Content-Type': 'application/json' }
        });
    } else {
        context.done(null, {
            body: info,
            headers: { 'Content-Type': 'application/json' }
        });
    }
};

FunctionsのJavascriptはnode.jsなので、node.jsのモジュールを使いたい場合は、

Function Appの設定から、Kuduに移動を選択します。

node_install

・・・/wwwroot/login-authに移動したら、underscoreを使えるようにします。

npm init -y

npm install underscore
スクリーンショット 2017-01-30 12.20.31

先ほどと同じような処理をunderscoreを使うと綺麗に使えて嬉しいです。

var _ = require('underscore');
 
module.exports = function (context, req) {
    var body = JSON.parse(req.body);

    if (!body || !body.loginId || !body.password) {
        context.done(null, {
            status: 400,
            body: { message: "parameter invalid." },
            headers: { 'Content-Type': 'application/json' }
        });
        return;
    }

    var info = _.where(context.bindings.Users, { UserId: body.loginId, Password: body.password });

    if (info.length === 0) {
        context.done(null, {
            status: 401,
            body: { message: "LoginId or Password invalid."},
            headers: { 'Content-Type': 'application/json' }
        });
    } else {
        context.done(null, {
            body: info[0],
            headers: { 'Content-Type': 'application/json' }
        });
    }
};

以上で、サーバーのAPIはできました。

最後にアプリからリクエストしてみたいと思います。

monacaでsampleアプリを作成します。

index.htmlを作成

<html>
<head>
    <meta charset="UTF-8">
    <title>Azure Functions Sample</title>
</head>
<body>
    <div style="text-align: center">
        <h1>Azure Functions Sample</h1>
        <p>ログインID</p>
        <p>
            <input id="login-id" type="text"/>
        </p>
        <p>パスワード</p>
        <p>
            <input id="password" type="password"/>
        </p>
        <button onclick="login();">ログイン</button>
    </div>
    <script src="./dist/main.js"></script>
</body>
</html>

main.jsを作成

var url = 'https://sample-functions.azurewebsites.net/api/login-auth?code=1qdh5Vh・・・';

function login() {
    var loginId = document.getElementById("login-id").value;
    var password = document.getElementById("password").value;
    
    var xhr = new XMLHttpRequest();
    var param = {
        loginId: loginId,
        password: password 
    }
    xhr.open('POST', url);
    xhr.setRequestHeader( 'Content-Type', 'application/x-www-form-urlencoded' );
    xhr.send(JSON.stringify(param));
    
    xhr.onreadystatechange = function() {
        var data = JSON.parse(xhr.responseText);
        if( xhr.status == 200) {
            alert( 'Success! :'+data.Name );
        } else {
            alert( data.message );
        }
    };   
}

monacaのプレビューで確認する場合、corsの設定をサーバー側でする必要があります。

Function Appの設定 > CORSの構成で、monacaプレビューのドメインを指定します。

スクリーンショット 2017-01-30 16.54.23

ログインID、パスワードにテーブルで登録したデータを入れて、ログインすると、

ユーザー名が返ってきました。

login

 

まとめ

Azure Functionsの活用方法としては、もっとライトにメンテナンスのJsonを返すとか、

UserAgentを見てApp StoreかGoogle Playにリダイレクトさせるとか色々ありますが、

モバイルアプリ開発でちょいとサーバー的なところが欲しいと思ったら、検討の価値ありかと。

ABOUTこの記事をかいた人

WEBアプリ開発をメイン業務としている。後輩エンジニアの育成リーダーとしても活躍中。社内勉強会の立ち上げなど、会社の前線でその能力を発揮している。2009年、株式会社ヘッドウォータース新卒入社。