Azure Functions の Proxy を調べてみた

Azure Functions の Proxy を試したので、記述しておきたい。

Azure Functions の Proxy は特定のファンクション毎に設定できるもので、ファンクションの持つ URL のサブディレクトリを指定すると、お好きなバックエンドに転送してくれる。URL が変わらないので、クライアントからは同じサービスに見える。

```
functions A:
    /hello -- HttpTriggerFunctinos
    /greeting  -- proxy ---  backend functions B/greeting
         :
```

# 外部ページへのルーティング

最初のサンプルは、バックエンドとして https://www.yahoo.co.jp にリクエストを転送してみよう。

![helloworld.png](https://qiita-image-store.s3.amazonaws.com/0/3470/b87544bf-8f52-bf4c-32bb-0e294aa0a793.png)

設定はこれだけ。試しに、Yahoo! Japan をバックエンドにしていますが、URLはAzure Functions のそれになっています。httpsもルーティングするみたいですね。

![yahoo.png](https://qiita-image-store.s3.amazonaws.com/0/3470/41e06884-eacc-3512-9099-122c71e3c317.png)

# 別の Azure Functions をバックエンドに持つ

次に HttpTrigger を持った別のAzure Functions を書いてみましょう。これはテンプレートでほとんど書かれているので、私はちょっと変えただけですが、質問を投げたら、暴言が返ってくる感じです。

```CSharp
using System.Net;

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

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

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

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

    return question == null
        ? req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a question on the query string or in the request body")
        : req.CreateResponse(HttpStatusCode.OK, $"Your question is '{question}', right. The answer is go to hell.");
}

```
## Route Template Parameter の指定による URL ルーティング

これを作ってから、先ほどの Proxy を作成した Functions から、Proxy してみます。ただ、プロキシするのは前にやっているので、Proxy の　Route Template Parameter という機能を使ってみます。

![backend01.png](https://qiita-image-store.s3.amazonaws.com/0/3470/680b7464-c82d-dae0-6802-6ab71a7e4c40.png)

1 では、Route Template として、 `backend/{question}` を指定しています。2 の Backend URL では、`https://japanfunctions.azurewebsites.net/api/SimpleHttpTrigger?code=...&question={question}` になっています。このProxy に、`https://samplefunction01.azurewebsites.net/backend/hi` とリクエストすると、バックエンドに、`https://japanfunctions.azurewebsites.net/api/SimpleHttpTrigger?code=...&question=hi` としてルーティングされるという仕組みで、URL を柔軟に変更して転送することができます。ちなみに、code= は、Azure Functions の http トリガを呼ぶときのアクセスキーです。デフォルトでcodeを無くしたら、401 Unauthorized が返ってきます。詳しくは[Working with Key](https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-http-webhook)に詳しいですが、function のすべてのfunction で有効な function keys と、特定の function のみで有効な host keys が存在します。万が一public なものを作りたい場合は、proxy してあげればよい。

![ok.png](https://qiita-image-store.s3.amazonaws.com/0/3470/c0893e0e-2a40-4db4-c329-9621d6f56ddf.png)

XML で正しく帰ってきているようです。Chromeだと、XML で帰ってきますが、Edge だと、違うようです。[ブログ](http://gunnarpeipman.com/2017/04/azure-functions-http/)を読むと、次の Accept を指定すると、思った通りのフォーマットで帰ってくるようです。何も指定しなければプレーンテキストで帰ってきますが、次のようにすると、Chrome の挙動と同じになりました。Accept ヘッダは受信できるフォーマットを指定できるものなので、それが反映されます。

![postman01.png](https://qiita-image-store.s3.amazonaws.com/0/3470/fe95aaa8-3cf3-b2c4-0eb9-fa597dccff03.png)

調べてみると、chrome の Developers tool で見てみると、どうやら、Chrome は、json を受け入れ可能ではなさそうです。`Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8`がAccept ヘッダの内容です。

![chrome01.png](https://qiita-image-store.s3.amazonaws.com/0/3470/3c639a97-ec3b-cacd-d0ea-1d5100497543.png)

Accept ヘッダに `application/json`を指定したり、Accept を指定しなければ、ちゃんとjson で帰ってくるようです。

![postman03.png](https://qiita-image-store.s3.amazonaws.com/0/3470/ea827911-c6e6-5172-4b70-96821e2c50a2.png)



## {question} と {*question} の違い

この Parameter には、* をつけることができます。つけるとどう変わるでしょうか？　

### {question} 指定時

例えばリクエストとして、`https://samplefunction01.azurewebsites.net/backend/hi/guys` をリクエストしてみると、{question}指定の場合は、マッチせずエラーになります。

![ng.png](https://qiita-image-store.s3.amazonaws.com/0/3470/7f31fe67-1a71-a3a6-800f-b95c8a311dd6.png)

{*question} を指定すると、配下の階層を受入れてくれます。

![ok2.png](https://qiita-image-store.s3.amazonaws.com/0/3470/90527d4b-22a9-eaf5-dbb2-04b794986ffc.png)

これは、バックエンドが、/aaa/bbb 型のリクエストになるときに有効と思われます。

# プロキシのモック

実はバックエンドなしでもレスポンスを返せます。また、バックエンドに送信するリクエストを、`requestOverrides` や、`responseOverrides` によって、編集できます。バックエンドに送信するときにヘッダをつけるとかできそうですね。ここでは、response.body を返すことができるので、バックエンドを指定せず、Advanced Editor のリンクをクリックして、直接編集してみましょう。私は`responseOverrides` の配下を追加しただけです。

![mock.png](https://qiita-image-store.s3.amazonaws.com/0/3470/25580d6f-f700-74b5-1c6b-4f6daca6d993.png)


```
{
    "$schema": "http://json.schemastore.org/proxies",
    "proxies": {
        "hello": {
            "matchCondition": {
                "route": "hello"
            },
            "backendUri": "https://www.yahoo.co.jp"
        },
        "backend": {
            "matchCondition": {
                "route": "backend/{*question}"
            },
            "backendUri": "https://japanfunctions.azurewebsites.net/api/SimpleHttpTrigger?code=6ybDI3Gg/XBBItC3vRRnVma19eHE85UH8cONZ0xJcQkxadWl8GuocQ==&question={question}"
        },
        "mock": {
            "matchCondition": {
                "route": "mock/{message}"
            },
            "responseOverrides": {
                "response.body": "echo:{message}. RequestMethod: {request.method} Environment: %FUNC_ENV%",
                "response.headers.Content-Type": "text/plain"
            }
        }
    }
}
```
このように記述すると、{request.method} のような形式でヘッダの内容をとってきたり、`%xxx%` で指定すると環境変数をとってきたりできます。環境変数は、Application Settings/App Settings で指定できます。ここでは、`FUNC_ENV`という環境変数をセットしてきました。

![header.png](https://qiita-image-store.s3.amazonaws.com/0/3470/f3a409fa-c41b-8644-85c9-ec002a038ec7.png)

しっかり予想通りの結果が返ってきました。つまり、C#のコードすら書かなくても、Mock 程度なら設定で終わります。やっぱ単純だけどサーバーレスって凄いなと思います。

次回は、Swap だとか、Functions の CI/CD や Testing について調べてみたいと思います。