前置き
Azure Functions の HTTP トリガーの関数では、API キーを関数 URL のクエリストリング(code
)に入れて実行しますね(承認レベルが匿名の場合は不要)。
https://example.azurewebsites.net/api/Function1?code=xxxxxxxx
この API キーを間違えたり、渡さなかったりすると 401 エラーが出るわけですが、今回はこのキーを間違えていないはずなのに 401 エラーが出てしまうケースを紹介します。
401 エラーになるコード
以下のような関数のコードを見てください。
承認レベルは Function で、関数ごとに異なる キーを渡して実行します。
これをデプロイして Function2 を実行すると、なんと正しい API キーを渡しても 401 エラーになります。
public static class SampleFunctions { [FunctionName(nameof(Function1))] public static IActionResult Function1( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "{name}")] HttpRequest req, string name) { return new OkObjectResult($"Function1: {name}"); } [FunctionName(nameof(Function2))] public static IActionResult Function2( [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequest req) { return new OkObjectResult("Function2: OK"); } }
さて、なぜだかわかりますか???
解説
ポイントは各関数メソッドの Route =
の部分です。
Route でパラメータを渡せる
Route
では、関数を呼び出す URL の 「https://example.azurewebsites.net/api/」以降のパスを指定することができます。
そして、{ }
でパラメータも渡すことができ、このパラメータの値は同じ名前の別の引数としてメソッド内部で利用可能です。
したがって Function1 のこの部分、
public static IActionResult Function1( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "{name}")] HttpRequest req, string name)
関数 URL としては
https://example.azurewebsites.net/api/JohnDoe?code=xxxxxxxx
のようになり、好きな文字列を渡して関数内で使うことができるようになります。
Route = null
ところが、Function2 では、Route = null
となっています。
Route
が null
の場合は、URL のパスに関数名が入ります。つまり、こんな URL になります。
https://example.azurewebsites.net/api/Function2?code=XXXXXXXX
もうおわかりですね
そう、
- Function1 の指定
Route = {name}
- Function2 の指定
Route = null
が、URL として完全にかぶってしまっているのです。
そして、定義として先にくる Function1 が優先され、「https://example.azurewebsites.net/api/{何かしらの文字列}」という Function1 の URL に当てはまる「https://example.azurewebsites.net/api/Function2」という Function2 の URL も、Function1 の呼び出しだと思われてしまうため、Function2 のキーを渡しても Function1 としては間違ったキーとなるので、401 エラーになってしまう、ということになります。
ちなみに
もし Function1
と Function2
の名前が逆だったら、つまり
- Function1 の指定
Route = null
- Function2 の指定
Route = {name}
という定義であれば、/Function1
という呼び出しは定義が先の Function1 と扱ってくれるため、どちらもエラーにならず 200 が返ってきます。
しかし、この場合でも Function2 の name
に「Function1」という文字列を渡そうとしたら……?
恐ろしいですね。
そんなことが起きないように
ということで、Route
を指定する場合は必ず固定のパスを含んだかたちで指定するようにしましょう。
ドキュメントのサンプルを見てもきっちりここは指定されているはずです。
[FunctionName(nameof(Function1))] public static IActionResult Function1( [HttpTrigger(AuthorizationLevel.Function, "get", Route = "Function1/{name}")] HttpRequest req, string name) { return new OkObjectResult($"Function1: {name}"); }
固定のパスを指定しなくても、そしてたとえ URL がかぶっていたとしてもデプロイや起動時にエラーになってくれないので、注意が必要です。