演習 - 関数アプリにロジックを追加する

完了

引き続き歯車駆動の例を使用し、温度サービス用のロジックを追加しましょう。 具体的には、HTTP 要求からデータを受信します。

関数の要件

まず、ロジックにいくつかの要件を定義する必要があります。

  • 0 から 25 度の温度には、OK のフラグを設定します。
  • 25 から 50 度の温度には、CAUTION (注意) のフラグを設定します。
  • 50 度以上の温度には、[危険] のフラグを設定します。

関数アプリに関数を追加する

前のユニットで説明したように、Azure には関数のビルドに役立つテンプレートが用意されています。 このユニットでは、HttpTrigger テンプレートを使って温度サービスを実装します。

  1. 前の演習で、関数アプリをデプロイし、それを開きました。 まだ開いていない場合は、[ホーム] ページで [すべてのリソース] を選択し、ご自分の関数アプリ (escalator-functions-xxx のような名前) を選択して開くことができます。

  2. Function App 画面の [関数] タブで、[Azure portal で作成] を選びます。 [関数の作成] ペインが表示されます。

  3. [テンプレートの選択] で、[HTTP トリガー] を選択します。

  1. [作成] を選択します HttpTrigger1 が作成され、HttpTrigger1 関数ペインに表示されます。

  2. 左側の [開発者] メニューで、[コードとテスト] を選択します。 コード エディターが開き、関数の index.js コード ファイルの内容が表示されます。 HTTP テンプレートによって生成された既定のコードが、次のスニペット内に表示されます。

    module.exports = async function (context, req) {
        context.log('JavaScript HTTP trigger function processed a request.');
    
        const name = (req.query.name || (req.body && req.body.name));
        const responseMessage = name
            ? "Hello, " + name + ". This HTTP triggered function executed successfully."
            : "This HTTP triggered function executed successfully. Pass a name on the query string or in the request body for a personalized response.";
    
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: responseMessage
        };
    }
    

    この関数は、HTTP 要求のクエリ文字列から、または要求本文の一部として名前が渡されることを想定しています。 この関数の応答で、"Hello, <name>. This HTTP triggered function executed successfully." (name さん、この HTTP トリガー関数は正常に実行されました。) というメッセージが返されます。このとき、要求で送信された name がエコー バックされます。

    ソース ファイルのドロップダウン リストから function.json を選択すると、次のコードのような関数の構成が表示されます。

    {
      "bindings": [
        {
          "authLevel": "function",
          "type": "httpTrigger",
          "direction": "in",
          "name": "req",
          "methods": [
            "get",
            "post"
          ]
        },
        {
          "type": "http",
          "direction": "out",
          "name": "res"
        }
      ]
    }
    

    この構成ファイルで、HTTP 要求を受け取ったときに実行する関数を宣言します。 出力バインディングを使用すると、HTTP 応答として送信される応答が宣言されます。

  1. [テンプレートの詳細] セクションの [新しい関数] フィールドに、「DriveGearTemperatureService」と入力します。 [承認レベル][関数] のままにして [作成] 選択し、関数を作成します。 DriveGearTemperatureService 関数の [概要] ペインが表示されます。

  2. [関数] メニューで、[Code + Test](コードとテスト) を選択します。 コード エディターが開き、run.ps1 コード ファイルの内容が表示されます。 テンプレートによって生成された既定のコードは、次のスニペット内に一覧表示されます。

    using namespace System.Net
    
    # Input bindings are passed in via param block.
    param($Request, $TriggerMetadata)
    
    # Write to the Azure Functions log stream.
    Write-Host "PowerShell HTTP trigger function processed a request."
    
    # Interact with query parameters or the body of the request.
    $name = $Request.Query.Name
    if (-not $name) {
        $name = $Request.Body.Name
    }
    
    $body = "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
    
    if ($name) {
        $body = "Hello, $name. This HTTP triggered function executed successfully."
    }
    
    # Associate values to output bindings by calling 'Push-OutputBinding'.
    Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
        StatusCode = [HttpStatusCode]::OK
        Body = $body
    })
    

    この関数は、HTTP 要求のクエリ文字列から、または要求本文の一部として名前が渡されることを想定しています。 HTTP 関数では、出力バインディングに書き込むことによって応答を生成する必要があります。これは、Push-OutputBinding コマンドレットを使用して PowerShell 関数で実行されます。 この関数では、メッセージ Hello $name が返され、要求で送信された名前がエコー バックされます。

  3. ソースのドロップダウン リストで function.json を選択すると、次のような関数の構成が表示されます。

    {
      "bindings": [
        {
          "authLevel": "function",
          "type": "httpTrigger",
          "direction": "in",
          "name": "Request",
          "methods": [
            "get",
            "post"
          ]
        },
        {
          "type": "http",
          "direction": "out",
          "name": "Response"
        }
      ]
    }
    

    この構成では、HTTP 要求を受け取ったときに実行する関数を宣言します。 出力バインディングを使用すると、HTTP 応答として送信される応答が宣言されます。

関数をテストする

ヒント

cURL は、ファイルを送受信するために使用できるコマンドライン ツールです。 このツールは、Linux、macOS、および Windows 10 に含まれており、その他のほとんどのオペレーティング システムでもダウンロードできます。 cURL では、HTTP、HTTPS、FTP、FTPS、SFTP、LDAP、TELNET、SMTP、POP3 などのさまざまなプロトコルがサポートされます。 詳細については、次のリンクを参照してください。

関数をテストするには、コマンド ラインで cURL を使用して、関数 URL に HTTP 要求を送信できます。

  1. トリガー関数ペインの下部にある [ログ] フレームを展開します。 ログ フレームの上部にあるドロップダウンの [ファイルシステム ログ] を選択します。 ログ フレームは、1 分ごとのトレース通知の取得を開始する必要があります。

  2. 関数のエンドポイント URL を確認するには、次の画像に示すように、コマンド バーで [関数の URL の取得] を選択します。 URL の末尾にある [クリップボードにコピー] アイコンを選択して、このリンクを保存します。 このリンクを後で使用できるように、メモ帳または同様のアプリに保存します。

    Screenshot of the Azure portal showing the function editor, with the Get function URL button highlighted.

  3. コマンド プロンプトを開き、cURL を実行して HTTP 要求を関数 URL に送信します。 前の手順でコピーした URL を使用してください。

    curl "<your-https-url>"
    

    ヒント

    URL 内の特殊文字で問題が発生しないよう、URL を引用符で囲む必要があります。
    Windows を使っている場合は、コマンド プロンプトから cURL を実行してください。 PowerShell には curl コマンドがありますが、それは Invoke-WebRequest の別名であり、cURL と同じものではありません。

    応答は次のようになります。

    This HTTP triggered function executed successfully. Pass a name on the query string or in the request body for a personalized response.
    

    今回は名前を要求で渡します。 そのためには、name というクエリ文字列パラメーターを URL に追加する必要があります。 次の例では、クエリ文字列パラメーター name=Azure を追加しています。

    curl "<your-https-url>&name=Azure"
    

    応答は次のようになります。

    Hello, Azure. This HTTP triggered function executed successfully.
    

    関数が正常に実行され、要求で渡した名前が返されました。

HTTP トリガーをセキュリティで保護する

HTTP トリガーを使用すると、API キーを使用して、要求の一部としてキーを要求することによって、不明な呼び出し元をブロックすることができます。 関数を作成するときは、認証レベルを選択します。 既定では、関数固有の API キーが必要な "関数" に設定されています。 また、"管理者" に設定してグローバルな "マスター" キーを使用したり、"Anonymous" に設定してキーが必要ないことを示したりすることもできます。 作成後に関数プロパティから認証レベルを変更することもできます。

この関数を作成したときに "関数" を指定しているため、HTTP 要求を送信するときにキーを指定する必要があります。 code という名前のクエリ文字列パラメーターとして送信できます。 または、推奨されるメソッドを使い、x-functions-key という名前の HTTP ヘッダーとして渡します。

  1. 関数とマスター キーを検索するには、Function App のメニューで [開発者] の下にある [関数キー] を選びます。 関数の [関数キー] ペインが開きます。

  2. 既定では、関数キー値は非表示です。 [値の表示] を選んで、既定の関数キー値を表示します。 後で使うことができるように、[値] フィールドの内容をクリップボードにコピーし、メモ帳などのアプリにこのキーを保存します。

    Screenshot showing the Function Keys pane with the revealed function key highlighted.

  3. 関数キーを使用して関数をテストするには、コマンド プロンプトを開き、cURL を実行して HTTP 要求を関数 URL に送信します。 保存した関数キーの値を <your-function-key> に置き換え、<your-https-url> を関数の URL に置き換えます。

    curl --header "Content-Type: application/json" --header "x-functions-key: <your-function-key>" --request POST --data "{\"name\": \"Azure Function\"}" <your-https-url>
    
  4. cURL コマンドを見直し、次の値があることを確認します。

    • application/json の種類の Content-Type ヘッダー値が追加されていること。
    • 関数キーがヘッダー値 x-functions-key として渡されていること。
    • POST 要求が使用されていること。
    • 関数の URL で Azure 関数を渡しました。
  5. ログを確認します。

    [コード + テスト] ペインで、ログ ファイルの出力が表示されているセッションが開きます ([ログ] ペインの上部にあるドロップダウンの [ファイルシステム ログ] が選択されていることを確認してください)。 ログ ファイルは要求の状態で更新され、次のようになります。

```output
2022-02-16T22:34:10.473 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=4f503b35-b944-455e-ba02-5205f9e8b47a)
2022-02-16T22:34:10.539 [Information] JavaScript HTTP trigger function processed a request.
2022-02-16T22:34:10.562 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=4f503b35-b944-455e-ba02-5205f9e8b47a, Duration=114ms)
```
```output
2022-02-16T21:07:11.340 [Information] INFORMATION: PowerShell HTTP trigger function processed a request.
2022-02-16T21:07:11.449 [Information] Executed 'Functions.DriveGearTemperatureService' (Succeeded, Id=25e2edc3-542f-4629-a152-cf9ed99680d8, Duration=1164ms)
```

関数にビジネス ロジックを追加する

受け取った測定温度を確認し、各温度の読み取りの状態を設定する関数にロジックを追加しましょう。

この関数では、温度測定値の配列が要求されます。 次の JSON スニペットは、この関数に送信する要求本文の例です。 reading エントリごとに、ID、タイムスタンプ、温度があります。

{
    "readings": [
        {
            "driveGearId": 1,
            "timestamp": 1534263995,
            "temperature": 23
        },
        {
            "driveGearId": 3,
            "timestamp": 1534264048,
            "temperature": 45
        },
        {
            "driveGearId": 18,
            "timestamp": 1534264050,
            "temperature": 55
        }
    ]
}

ビジネス ロジックを実装するため、関数内の既定のコードを次のコードに置き換えましょう。

[HttpTrigger1] 関数ペインで index.js ファイルを開き、次のコードで置き換えます。 この変更を加えた後、コマンド バーの [保存] を選び、更新内容をファイルに保存します。

module.exports = function (context, req) {
    context.log('Drive Gear Temperature Service triggered');
    if (req.body && req.body.readings) {
        req.body.readings.forEach(function(reading) {

            if(reading.temperature<=25) {
                reading.status = 'OK';
            } else if (reading.temperature<=50) {
                reading.status = 'CAUTION';
            } else {
                reading.status = 'DANGER'
            }
            context.log('Reading is ' + reading.status);
        });

        context.res = {
            // status: 200, /* Defaults to 200 */
            body: {
                "readings": req.body.readings
            }
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please send an array of readings in the request body"
        };
    }
    context.done();
};

ここで追加したロジックは簡単です。 配列を反復処理し、温度フィールドの値に応じて OKCAUTIONDANGER のいずれかの状態を設定します。 次に、各エントリに追加された状態フィールドと共に、測定値の配列を返します。

ペインの下部にある [ログ] を展開するときは、Log ステートメントに注意してください。 関数が実行されると、これらのステートメントによって [ログ] ウィンドウにメッセージが追加されます。

run.ps1 ファイルを開き、その内容を次のコードに置き換えます。 この変更を加えた後、コマンド バーの [保存] を選び、更新内容をファイルに保存します。

using namespace System.Net

param($Request, $TriggerMetadata)

Write-Host "Drive Gear Temperature Service triggered"

$readings = $Request.Body.Readings
if ($readings) {
    foreach ($reading in $readings) {
        if ($reading.temperature -le 25) {
            $reading.Status = "OK"
        }
        elseif ($reading.temperature -le 50) {
            $reading.Status = "CAUTION"
        }
        else {
            $reading.Status = "DANGER"
        }

        Write-Host "Reading is $($reading.Status)"
    }

    $status = [HttpStatusCode]::OK
    $body = $readings
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please send an array of readings in the request body"
}

Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})

ここで追加したロジックは簡単です。 配列を反復処理し、温度フィールドの値に応じて OKCAUTIONDANGER のいずれかの状態を設定します。 次に、各エントリに追加された状態フィールドと共に、測定値の配列を返します。

Write-Host コマンドレットへの呼び出しに注意してください。 関数が実行されると、これらのステートメントによって [ログ] ウィンドウにメッセージが追加されます。

ビジネス ロジックをテストする

ここでは、[開発者]>[Code + Test](コードとテスト)[Test/Run](テストと実行) 機能を使い、関数をテストします。

  1. [入力] タブで [本文] テキスト ボックスの内容を次のコードに置き換えて、サンプル要求を作成します。

    {
        "readings": [
            {
                "driveGearId": 1,
                "timestamp": 1534263995,
                "temperature": 23
            },
            {
                "driveGearId": 3,
                "timestamp": 1534264048,
                "temperature": 45
            },
            {
                "driveGearId": 18,
                "timestamp": 1534264050,
                "temperature": 55
            }
        ]
    }
    
  2. [実行] を選択します。 [出力] タブに HTTP 応答コードと内容が表示されます。 ログ メッセージを表示するには、ペインの下部にあるポップアップで [ログ] タブを開きます (まだ開いていない場合)。 次の画像には、出力ペインに応答の例、[ログ] ペインにメッセージが示されています。

    Screenshot of the Azure function editor, with the Test and Logs tabs showing.

    [出力] タブは、状態フィールドが各測定値に正しく追加されたことを示しています。

  3. 左側の [開発者] メニューで [モニター] を選択し、要求が Application Insights にログ記録されていることを確認します。 関数の [監視] ペインが表示されます。

    このペインの [呼び出し] タブには、関数呼び出しごとに呼び出しトレースが表示されます。 いずれかの呼び出しの Date(UTC) 値を選択し、関数の実行に関する詳細を表示します。