Azure FunctionsでCosmos DBを入力バインドする

以前「Azure FunctionsからCosmos DBに出力バインドする」という記事を書きましたので、それと対になる形で入力バインドについても簡単にまとめておこうと思います。

ryuichi111std.hatenablog.com

今回 実装することは以下の通りです。

  • HttpTriggerを持つAzure Functionsを作成
  • Cosmos DBドキュメントを入力バインドで受け取る
  • Function内の処理で、受け取ったCosmos DBドキュメントに変更を加える

1 開発環境

利用する環境は、以下の通りです。

2 前提条件

以下のような Cosmos DB アカウント を用意している前提とします。

f:id:daigo-knowlbo:20170814014414p:plain

  • APIモデルは「DocumentDB」
  • データベース名「CompanyDB」、コレクション名「EmployeeCollection」を作成済
  • ドキュメントが1件登録済み(id=“0000000001")
-- 登録済みドキュメント --
{
  "id": "0000000001",
  "firstname": "ryuichi",
  "lastname": "daigo",
  "_rid": "5T9QALZ5bQABAAAAAAAAAA==",
  "_self": "dbs/5T9QAA==/colls/5T9QALZ5bQA=/docs/5T9QALZ5bQABAAAAAAAAAA==/",
  "_etag": "\"0300aeae-0000-0000-0000-599081c20000\"",
  "_attachments": "attachments/",
  "_ts": 1502642619
}

3 実装

Visual Studio 2017 Preview(15.3)を起動します。

(1) プロジェクト作成

メニュー「ファイル → 新規作成 → プロジェクト」を選択します。

「新しいプロジェクト」ウィンドウが表示されます。
以下の入力を行います。

  • プロジェクトテンプレート:「Visual C# → Cloud → Azure Functions」
  • 名前:適当に・・・ここではデフォルトの FunctionApp1 にしました

f:id:daigo-knowlbo:20170814014959p:plain

設定用JSONのみの空っぽのソリューションが出来上がります。

f:id:daigo-knowlbo:20170814015222p:plain

(2) Functionsコードの追加

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「追加 → 新しい項目」を選択します。

f:id:daigo-knowlbo:20170814015448p:plain

「新しい項目の追加」ウィンドウが表示されるので、「Azure Function」アイテムを追加します。
ここでは名前をデフォルトの「Function1.cs」としました。

f:id:daigo-knowlbo:20170814015604p:plain

「New Templates - Function1」というファンクションのトリガーを選択するウィンドウが表示されます。

f:id:daigo-knowlbo:20170814015713p:plain

「HttpTriggerWithParameters」を選択します。
また、「AccessRights」を「Anonumouse」としました。これにより、トリガーとなるHTTPリクエストを行う際に認証なしとなります(テストの利便性の為ここではそうしていますが、誰でもHTTPトリガーをキックできてしまうということなので、ご利用には気を付けてください)。
「FunctionName」はデフォルトの「HttpTriggerWithParametersCSharp」とします。

自動生成された Function1.cs 配下の通りです(リスト1)。

//リスト1 自動生成されたFunction1.cs

using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace FunctionApp1
{
  public static class Function1
  {
    [FunctionName("HttpTriggerWithParametersCSharp")]

    public static HttpResponseMessage Run(
      [HttpTrigger(
        AuthorizationLevel.Anonymous, 
        "get",
        "post", 
        Route = "HttpTriggerCSharp/name/{name}")]
      HttpRequestMessage req, 
      string name, 
      TraceWriter log)
    {
      log.Info("C# HTTP trigger function processed a request.");

      // Fetching the name from the path parameter in the request URL
      return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
    }
  }
}

Run()メソッドがFunctionのエントリーポイントです。
第1引数「HttpRequestMessage req」に「[HttpTrigger]属性」が付与されています。HttpTrigger属性のパラメータで以下の設定が宣言されています。

(3) CosmosDB入力バインド実装の追加

まず、CosmosDB入力バインド用エクステンションを NuGetパッケージ より追加します。

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「NuGet パッケージの管理」を選択します。

f:id:daigo-knowlbo:20170814021042p:plain

「NuGet パッケージの管理」画面が表示されたら、「参照」タブを選択し「Microsoft.Azure.WebJobs.Extensions.DocumentDB」を検索します。

f:id:daigo-knowlbo:20170814021317p:plain

Microsoft.Azure.WebJobs.Extensions.DocumentDB」を選択して、インストールボタンをクリックします。

では、 Function1.cs を開き、以下のように編集します。

// リスト2 CosmosDB入力バインド処理を追加したFunction1.cs

using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;

namespace FunctionApp1
{
  public static class Function1
  {
    [FunctionName("HttpTriggerWithParametersCSharp")]

    public static HttpResponseMessage Run(
      [HttpTrigger(
        AuthorizationLevel.Anonymous, 
        "get",
        "post", 
        Route = "HttpTriggerCSharp/name/{name}")]
      HttpRequestMessage req,
      [DocumentDB(
        "CompanyDB", 
        "EmployeeCollection",
        ConnectionStringSetting = "cosmosdb_DOCUMENTDB", 
        Id = "{name}")]
      dynamic document,
      string name, 
      TraceWriter log)
    {
      log.Info("C# HTTP trigger function processed a request.");

      // 入力ドキュメントをログ出力
      log.Info("input document info");
      log.Info(" firstname:" + document.firstname);
      log.Info(" lastname:" + document.lastname);

      // 入力ドキュメントに変更を加える
      document.age = 25;
      document.Salary = 15000000;

      // Fetching the name from the path parameter in the request URL
      return req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
    }
  }
}

Function1.csへの修正ポイントは以下の通りです。

  • 引数「document」の追加
    HttpTrigger引数に続けて「document引数」を追加しています。これが CosmosDB入力バインドパラメータになります。
    パラメータには「DocumentDB属性」を付与しています。
    入力バインドの情報として「データベース名」「コレクション名」を指定しています。
    「ConnectionStringSetting」は、Cosmos DBへの接続文字列情報です。接続文字列自体ではなく、接続文字列を設定した「構成情報キー」を指定します(設定方法は後述)。
    「Id」は、入力バインドするCosmos DBドキュメントの ID を表します。ここでは「{name}」としていますが、これはHttpTriggerのパラメータ「{name}」に該当します。「nameパラメータ値 = ドキュメントID」と、意味的に少し変ですが、自動生成コードの修正を最小限にしています。

  • documentのログ出力
    document引数には ID に該当するドキュメント オブジェクトが自動的にバインド設定されて呼び出されます。
    document.firstname / document.lastname をログ出力することで、内容を確認できるようにしています。

  • documentへのプロパティ値追加
    以下のようにdocumentに対して変更を加えています。
    入力バインドしたドキュメントへの変更は、Functionの正常終了後に Cosmos DBドキュメント に反映されます(データ更新が行われます)。

// 入力ドキュメントに変更を加える
document.age = 25;
document.Salary = 15000000;

(4) 公開 & Azureポータルでの構成

では、Azure上に公開します。

ソリューションエクスプローラで FunctionApp1 をマウス右ボタンクリック。
表示されたメニューから「公開」を選択します。

f:id:daigo-knowlbo:20170814022920p:plain

「新規作成」を選択して「発行」ボタンをクリックします(既にAzure FunctionsをAzure上に作成済みの場合には、「既存のものを選択」を利用します)。

f:id:daigo-knowlbo:20170814022953p:plain

Functionの名前等は適当に・・・ここでは Function Appの名前を InputBindExample として、「リソースグループ」「App Service プラン」「ストレージアカウント」は、それぞれ新規作成しました。

f:id:daigo-knowlbo:20170814023745p:plain

「公開」が成功した後の、Azureポータル画面が以下となります。
「Azure Functions(App Service)」「App Service Plan」「ストレージアカウント」の3つのリソースが作成されました。

f:id:daigo-knowlbo:20170814023859p:plain

Azure Functions「InputBindExample」を選択します。
「アプリケーション設定」をクリックして、Cosmos DBへの接続文字列項目を追加します。

f:id:daigo-knowlbo:20170814023941p:plain

f:id:daigo-knowlbo:20170814024043p:plain

(5) 実行

では、Functionを実行してみます。
まず、Azureポータルから実行します。

Function「HttpTriggerWithParametersCSharp」を選択します。
そして右側のテストタブを表示し、パラメータ「name」に「0000000001」を設定します。これは「入力パラメータname = ドキュメントID」として実装しているためです。
実行ボタンをクリックします。

f:id:daigo-knowlbo:20170814030723p:plain

出力されたログが以下の通りです。
ID = 0000000001 のドキュメント内容 firstname / lastname が出力されていることを確認することができます。つまり、確かにCosmos DBドキュメントが入力バインドパラメータとして引き渡されました。

f:id:daigo-knowlbo:20170814030753p:plain

次にAzureポータルで、Cosmos DBの「データエクスプローラ」を表示します。
age / salary 項目が追加されていることが確認できます。

f:id:daigo-knowlbo:20170814030920p:plain

匿名認証OK、HTTP GET許可としている為、ブラウザで「Https://inputbindexample.azurewebsites.net/api/HttpTriggerCSharp/name/0000000001」をアクセスする事でもFunctionをキックすることができます。

f:id:daigo-knowlbo:20170814033200p:plain

4 まとめ

ということで、Azure FunctionsへのCosmos DB入力バインドでした。
入力バインドといっても、Functionsの処理内でCosmos DBドキュメントに対して変更を加えることもできました。

Azure Functionsへの入出力バインドはCosmos DB以外にも「Blob storage」だったり、「Queue storage」だったりと、たくさんのバリエーションがあります。これらのバインドを利用すると、従来型の定型的ロジックの記述を省略することができます。
今回のCosmos DBバインドの例では、Cosmos DBへの接続処理の記述、取得・更新処理 の記述といったものが不要となりました。
つまり、煩雑なロジックの記述が Azure上のプラットフォームインフラ に吸収されていることにより、開発者はビジネスロジックの記述に注力することが可能になります。

本投稿では、ローカル実行やデバッグ等、色々端折っておりますが、Azure FunctionsへのCosmos DB入力バインドの一例として参考になればと思います。