Azure Functions における HTTP と Webhook のバインド

この記事では、Azure Functions で HTTP トリガーとバインドを構成および操作する方法について説明します。 これらと Azure Functions を使用して、サーバーなしの API を構築し、webhook に応答することができます。

Azure Functions には、以下のバインドが用意されています。

  • HTTP トリガーでは、HTTP 要求で関数を呼び出すことができます。 これを webhook に応答するようにカスタマイズすることができます。
  • HTTP 出力バインドでは、要求に応答することができます。

これは、Azure Functions の開発者向けリファレンス情報です。 Azure Functions を初めて使用する場合は、先に次のリソースを参照してください。

ヒント

HTTPClient のベスト プラクティスに関するドキュメントを読むことをお勧めします。

HTTP トリガー

HTTP トリガーは、HTTP 要求に応答して、関数を実行します。 これを、特定の URL または HTTP メソッドのセットに応答するようにカスタマイズできます。 HTTP トリガーも、webhook に応答するように構成することができます。

Functions ポータルを使用している場合は、事前作成されたテンプレートをすぐに使用し始めることもできます。 [新しい関数] を選択し、[シナリオ] ドロップダウンで [API と webhook] を選択します。 いずれかのテンプレートを選択し、[作成] をクリックします。

既定では、HTTP トリガーは要求に HTTP 200 OK 状態コードおよび空の本文で応答します。 応答を変更するには、HTTP 出力バインドを構成します。

HTTP トリガーの構成

HTTP トリガーを定義するには、次のような JSON オブジェクトを function.json の bindings 配列に含めます。

{
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
    "authLevel": "function",
    "methods": [ "GET" ],
    "route": "values/{id}"
},

バインドでは以下のプロパティがサポートされています。

  • name: 必須 - 要求または要求本文の関数コードで使用される変数名。 「コードからの HTTP トリガーの操作」を参照してください。
  • type: 必須 - "httpTrigger" に設定する必要があります。
  • direction: 必須 - "in" に設定する必要があります。
  • authLevel: 関数を呼び出すには要求にどのキーが存在する必要があるかを決定します。 後の「キーの操作」を参照してください。 値は次のいずれかになります。
    • anonymous: API キーは必要ありません。
    • function: 関数固有の API キーが必要です。 何も指定されなかった場合は、これが既定値になります。
    • admin: マスター キーが必要です。
  • methods: 関数が応答する HTTP メソッドの配列です。 指定しない場合、関数はすべての HTTP メソッドに応答します。 「HTTP エンドポイントのカスタマイズ」を参照してください。
  • route: 関数がどの要求 URL に応答するかを制御するルート テンプレートを定義します。 何も指定しなかった場合の既定値は <functionname> です。 「HTTP エンドポイントのカスタマイズ」を参照してください。
  • webHookType: 指定したプロバイダーの webhook レシーバーとして機能する HTTP トリガーを構成します。 これを選択した場合は、methods プロパティを設定しないでください。 「webhook への応答」を参照してください。 値は次のいずれかになります。
    • genericJson: 特定のプロバイダー用のロジックを持たない汎用 webhook エンドポイントです。
    • github: 関数は GitHub webhook に応答します。 これを選択した場合は、authLevel プロパティを設定しないでください。
    • slack: 関数は Slack webhook に応答します。 これを選択した場合は、authLevel プロパティを設定しないでください。

コードからの HTTP トリガーの操作

C# および F# の関数では、トリガー入力の型を HttpRequestMessage 型かカスタム型として宣言できます。 HttpRequestMessage を選択した場合は、要求オブジェクトへのフル アクセスが取得されます。 カスタム型 (POCO など) の場合、関数は要求本文を JSON として解析し、オブジェクトのプロパティに値を設定しようとします。

Node.js 関数の場合、Functions ランタイムは request オブジェクトではなく、要求本文を提供します。

使用例については、「HTTP トリガーのサンプル」を参照してください。

HTTP 応答出力バインド

HTTP 要求送信者に応答するには、HTTP 出力バインドを使用します。 このバインドには、HTTP トリガーが必要です。このバインドを使用すると、トリガーの要求に関連付けられている応答をカスタマイズすることができます。 HTTP 出力バインドが指定されない場合、HTTP トリガーは HTTP 200 OK と空の本文を返します。

HTTP 出力バインドの構成

HTTP 出力バインドを定義するには、次のような JSON オブジェクトを function.json の bindings 配列に含めます。

{
    "name": "res",
    "type": "http",
    "direction": "out"
}

バインドには、以下のプロパティが含まれています。

  • name: 必須 - 応答の関数コードで使用される変数名。 「コードからの HTTP 出力バインドの操作」を参照してください。
  • type: 必須 - "http" に設定する必要があります。
  • direction: 必須 - "out" に設定する必要があります。

コードからの HTTP 出力バインドの操作

http または webhook の呼び出し元に応答するために、出力パラメーター ("res" など) を使用できます。 また、応答を返すために、標準の Request.CreateResponse() (C#) または context.res (Node.JS) パターンを使用することもできます。 後のメソッドの使用方法の例については、「HTTP トリガーのサンプル」および「webhook のサンプル」を参照してください。

webhook への応答

webHookType プロパティのある HTTP トリガーは、webhooks に応答するように構成されます。 基本的な構成では、"genericJson" 設定を使用します。 この設定では、要求が、HTTP POST を使用していてコンテンツの種類が application/json であるものだけに制限されます。

トリガーは、さらに特定の webhook プロバイダー (GitHubSlack など) 用にカスタマイズできます。 プロバイダーが指定されている場合は、Functions ランタイムがプロバイダーの検証ロジックを処理できます。

webhook プロバイダーとしての GitHub の構成

GitHub webhook に応答するには、まず、HTTP トリガーで関数を作成し、webHookType プロパティを "github" に設定します。 次に、その URLAPI キーを GitHub リポジトリの [Webhook の追加] ページにコピーします。 詳細については、GitHub のドキュメント「Creating Webhooks (webhook の作成)」を参照してください。

webhook プロバイダーとしての Slack の構成

Slack webhook では、指定しなくてもトークンが自動的に生成されます。そのため、Slack によって生成されたトークンで、関数固有のキーを構成する必要があります。 「キーの操作」を参照してください。

HTTP エンドポイントのカスタマイズ

既定では、HTTP トリガーまたは WebHook の関数を作成する際に、次の形式のルートを使用して関数のアドレスを指定できます。

http://<yourapp>.azurewebsites.net/api/<funcname> 

HTTP トリガーの入力バインドで省略可能な route プロパティを使用すると、このルートをカスタマイズできます。 たとえば、次の function.json ファイルでは HTTP トリガーの route プロパティを定義します。

    {
      "bindings": [
        {
          "type": "httpTrigger",
          "name": "req",
          "direction": "in",
          "methods": [ "get" ],
          "route": "products/{category:alpha}/{id:int?}"
        },
        {
          "type": "http",
          "name": "res",
          "direction": "out"
        }
      ]
    }

この構成を使用すると、元のルートではなく、次のルートを使用して関数のアドレスを指定できるようになります。

http://<yourapp>.azurewebsites.net/api/products/electronics/357

これにより、関数のコードはアドレス内で "category" と "id" という 2 つのパラメーターをサポートできます。 パラメーターでは任意の Web API ルート制約を使用できます。 次の C# 関数コードは両方のパラメーターを使用します。

    public static Task<HttpResponseMessage> Run(HttpRequestMessage request, string category, int? id, 
                                                    TraceWriter log)
    {
        if (id == null)
           return  req.CreateResponse(HttpStatusCode.OK, $"All {category} items were requested.");
        else
           return  req.CreateResponse(HttpStatusCode.OK, $"{category} item with id = {id} has been requested.");
    }

同じルート パラメーターを使用する Node.js 関数コードを次に示します。

    module.exports = function (context, req) {

        var category = context.bindingData.category;
        var id = context.bindingData.id;

        if (!id) {
            context.res = {
                // status: 200, /* Defaults to 200 */
                body: "All " + category + " items were requested."
            };
        }
        else {
            context.res = {
                // status: 200, /* Defaults to 200 */
                body: category + " item with id = " + id + " was requested."
            };
        }

        context.done();
    } 

既定では、すべての関数のルートには api というプレフィックスが付きます。 host.json ファイルで http.routePrefix プロパティを使用すると、このプレフィックスをカスタマイズまたは削除できます。 次の例では、host.json ファイル内でプレフィックスに空の文字列を使用することで、api ルート プレフィックスを削除します。

    {
      "http": {
        "routePrefix": ""
      }
    }

関数の host.json ファイルを更新する方法の詳細については、「関数アプリ ファイルを更新する方法」を参照してください。

host.json ファイルで構成できるその他のプロパティについては、host.json のリファレンスを参照してください。

キーの操作

HttpTrigger は、セキュリティを強化するためにキーを利用できます。 標準的な HttpTrigger は、それらを API キーとして使用できます。キーは、要求内に存在する必要があります。 webhook はキーを使用して、プロバイダーで何がサポートされているかに応じてさまざまな方法で要求を承認することができます。

キーは関数アプリの一部として Azure に格納され、保存中は暗号化されます。 キーを表示するには、新しいキーを作成するか、キーを新しい値にロールし、ポータル内でいずれかの関数に移動して、[管理] を選択します。

キーには、次の 2 つの種類があります。

  • ホスト キー: これらのキーは、関数アプリ内のすべての関数で共有されます。 API キーとして使用した場合は、関数アプリ内のすべての関数がアクセスできます。
  • 関数キー: これらのキーは、それらが定義されている特定の関数にのみ適用されます。 API キーとして使用した場合は、その関数だけがアクセスできます。

各キーには、参照用に名前が付けられており、関数レベルおよびホスト レベルで "default" という名前の既定のキーがあります。 マスター キーは "_master" という名前の既定のホスト キーであり、各関数アプリに対して定義されています。これを取り消すことはできません。 このキーによって、ランタイム API に対する管理アクセスが可能になります。 バインド JSON で "authLevel": "admin" を使用するには、要求内にこのキーが存在する必要があります。他のキーである場合は、承認エラーになります。

注意

マスター キーは管理者特権を付与するものであるため、このキーを第三者と共有したり、ネイティブ クライアント アプリケーションで配布したりしないでください。 管理者承認レベルを選択する場合は注意が必要です。

API キーの承認

既定では、HttpTrigger には HTTP 要求内の API キーが必要です。 そのため、HTTP 要求は、通常は次のようになります。

https://<yourapp>.azurewebsites.net/api/<function>?code=<ApiKey>

キーは、上記のように code という名前のクエリ文字列変数に含めることも、x-functions-key HTTP ヘッダーに含めることもできます。 キーの値には、関数のために定義されている任意の関数キーまたは任意のホスト キーを指定できます。

バインド JSON の authLevel プロパティを変更することによって、キーを持たない要求を許可するか、マスター キーを使用する必要があることを指定するかを選択することができます (「HTTP トリガー」を参照してください)。

キーと webhook

webhook の承認は、HttpTrigger の一部である webhook レシーバー コンポーネントによって処理されますが、そのメカニズムは webhook の種類によって異なります。 ただし、各メカニズムはキーに依存します。 既定では、"default" という名前の関数キーが使用されます。 別のキーを使用する場合は、次のいずれかの方法で、要求と共にキー名を送信するように webhook プロバイダーを構成する必要があります。

  • クエリ文字列: プロバイダーが clientid クエリ文字列パラメーターでキー名を渡します (たとえば https://<yourapp>.azurewebsites.net/api/<funcname>?clientid=<keyname>)。
  • 要求ヘッダー: プロバイダーが x-functions-clientid ヘッダーでキー名を渡します。
注意

関数キーが、ホスト キーよりも優先されます。 2 つのキーが同じ名前で定義されている場合は、関数キーが使用されます。

HTTP トリガーのサンプル

function.json の bindings 配列に次の HTTP トリガーがあるとします。

{
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
    "authLevel": "function"
},

クエリ文字列または HTTP 要求の本文で name パラメーターを検索する、言語固有のサンプルを参照してください。

C での HTTP トリガーのサンプル#

using System.Net;
using System.Threading.Tasks;

public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log)
{
    log.Info($"C# HTTP trigger function processed a request. RequestUri={req.RequestUri}");

    // parse query parameter
    string name = req.GetQueryNameValuePairs()
        .FirstOrDefault(q => string.Compare(q.Key, "name", true) == 0)
        .Value;

    // Get request body
    dynamic data = await req.Content.ReadAsAsync<object>();

    // Set name to query string or body data
    name = name ?? data?.name;

    return name == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, "Hello " + name);
}

F での HTTP トリガーのサンプル#

open System.Net
open System.Net.Http
open FSharp.Interop.Dynamic

let Run(req: HttpRequestMessage) =
    async {
        let q =
            req.GetQueryNameValuePairs()
                |> Seq.tryFind (fun kv -> kv.Key = "name")
        match q with
        | Some kv ->
            return req.CreateResponse(HttpStatusCode.OK, "Hello " + kv.Value)
        | None ->
            let! data = Async.AwaitTask(req.Content.ReadAsAsync<obj>())
            try
                return req.CreateResponse(HttpStatusCode.OK, "Hello " + data?name)
            with e ->
                return req.CreateErrorResponse(HttpStatusCode.BadRequest, "Please pass a name on the query string or in the request body")
    } |> Async.StartAsTask

次のように、NuGet を使用して FSharp.Interop.Dynamic および Dynamitey アセンブリを参照する project.json ファイルが必要です。

{
  "frameworks": {
    "net46": {
      "dependencies": {
        "Dynamitey": "1.0.2",
        "FSharp.Interop.Dynamic": "3.0.0"
      }
    }
  }
}

このファイルは NuGet を使用して依存関係を取得し、それをスクリプト内で参照します。

Node.js での HTTP トリガーのサンプル

module.exports = function(context, req) {
    context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);

    if (req.query.name || (req.body && req.body.name)) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name || req.body.name)
        };
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
    }
    context.done();
};

webhook のサンプル

function.json の bindings 配列に次の webhook トリガーがあるとします。

{
    "webHookType": "github",
    "name": "req",
    "type": "httpTrigger",
    "direction": "in",
},

GitHub の問題のコメントを記録する、言語固有のサンプルを参照してください。

C での webhook のサンプル#

#r "Newtonsoft.Json"

using System;
using System.Net;
using System.Threading.Tasks;
using Newtonsoft.Json;

public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
    string jsonContent = await req.Content.ReadAsStringAsync();
    dynamic data = JsonConvert.DeserializeObject(jsonContent);

    log.Info($"WebHook was triggered! Comment: {data.comment.body}");

    return req.CreateResponse(HttpStatusCode.OK, new {
        body = $"New GitHub comment: {data.comment.body}"
    });
}

F での webhook のサンプル#

open System.Net
open System.Net.Http
open FSharp.Interop.Dynamic
open Newtonsoft.Json

type Response = {
    body: string
}

let Run(req: HttpRequestMessage, log: TraceWriter) =
    async {
        let! content = req.Content.ReadAsStringAsync() |> Async.AwaitTask
        let data = content |> JsonConvert.DeserializeObject
        log.Info(sprintf "GitHub WebHook triggered! %s" data?comment?body)
        return req.CreateResponse(
            HttpStatusCode.OK,
            { body = sprintf "New GitHub comment: %s" data?comment?body })
    } |> Async.StartAsTask

Node.JS での webhook のサンプル

module.exports = function (context, data) {
    context.log('GitHub WebHook triggered!', data.comment.body);
    context.res = { body: 'New GitHub comment: ' + data.comment.body };
    context.done();
};

次のステップ

Azure Functions のその他のバインドおよびトリガーの詳細については、「 Azure Functions のトリガーとバインドの開発者用リファレンス