Blog ブログ
へっぽこプログラマの奮闘記 第5回:要求に一致する複数のアクション??
どうもこんにちは。プログラマの田中智大です。
4月になり、やっと暖かくなった感じですが、油断していると時々雨が降って肌寒い夜がくるので油断できないですね。
さて今回は Azure の WebApps でサーバープログラムを書いてた時に起きた事についてお話します。
まず最初に言及しときますけど、無駄に関数を public にするなよ!とかいうのは今はおいておいてください。
内容としては Http の POST のリクエストが送られた時に行われる処理で hoge() という関数を使いたかったものになります。
このプログラムをデプロイした後、Http で要求を送った時にこんなエラーが出てしまったのです。
"ExceptionMessage":"要求に一致する複数のアクションが見つかりました: rn型 Controller の hogern型 Controller の Post"
「ちゃんと Post メソッド作って [HttpPost] 属性もつけたのになんで動かないんだ?」といった問題が発生してしまいました。
◆何が起きたの?
今回このエラーが表示された原因は ASP.NET がコントローラーのアクションを選択する時の動きが関わっています。
・まずは ASP.NET の動きから
ASP.NET は HTTP の要求を受け取った時、WebAPI にマッピングされているルートの情報から、サーバープログラム上の呼び出す public なコントローラー(クラス)を選択します。その後、そのコントローラーの中にあるアクション(メソッド)を実行します。
・次にドキュメントを読んでみましょう
ASP.NET Web APIでのルーティングとアクションの選択(英語)
この MSDN のドキュメントの「Action Selection」の項目にこんな一文があります
Which methods on the controller are considered “actions”? When selecting an action, the framework only looks at public instance methods on the controller.
翻訳すると「どのメソッドが”アクション”とみなされますか?フレームワークはコントローラ上で見つかった public メソッドのみを”アクション”として選択します」というものです。
さらに続きを読んでみましょう。
HTTP Methods. The framework only chooses actions that match the HTTP method of the request, determined as follows:
1.You can specify the HTTP method with an attribute: AcceptVerbs, HttpDelete, HttpGet, HttpHead, HttpOptions, HttpPatch, HttpPost, or HttpPut.
2.Otherwise, if the name of the controller method starts with “Get”, “Post”, “Put”, “Delete”, “Head”, “Options”, or “Patch”, then by convention the action supports that HTTP method.
3.If none of the above, the method supports POST.
これは、噛み砕いて説明すると、
アクションがどの HTTP の要求を処理するかは
1. [HttpPost] 等の特定の属性をつけているメソッド
2. PostやGetといった HTTP メソッド名から名前が始まるメソッド
3. 上記のいずれにも該当しない場合は POST メソッドをサポートします
の3つの条件で決まる事を意味しています。
つまり、今回ASP.NETは1つ目に説明した動作からコントローラー上で見つかった public なメソッドの Post() と hoge() を要求に対するアクションとみなしました。
そして2つ目に説明した動作から Post() は[HttpPost] の属性から POST要求を処理するアクション とみなされ、hoge() は1と2の条件に当てはまらなかったため、hoge() も POST要求を処理するアクション とみなされたのです。
これにより、コントローラー上に2つの POST のアクションが存在することになり、結果「要求に一致する複数のアクションが見つかりました」というエラーが発生したのです。
[HttpPost] をつけたらそれだけでそのメソッドのみが POST を処理してくれるというわけではないのですね。
◆どうすればいいの?
これを回避する方法は単純に「無駄に関数を public にするなよ!」というところではあるのですが、もしどうしてもアクションとみなされない public なメソッドを用意したいといった状況がある場合は [NonAction] という属性をつけてあげるとアクションとして選択されなくなるため、エラーは発生しなくなります。
◆おわりに
今回は、ASP.NETのアクションの選択が [HttpPost] によって決まるというわけではない事が分かりました。
また別の話になりますが、不用意にメソッドを public にした事によって ASP.NET のアクションの選択に影響が出る事も分かりました。みなさんにはオブジェクト指向のカプセル化を無視して、よく関数を public にするような時期はありませんでしたでしょうか?
カプセル化もそうですけど、不用意に関数を public にするとフレームワークによっては予期せぬ問題が起きてしまうことがあるのです。今回のケースもいい教訓になりました。
さて、大事な事だったので最後にもう一度言いましょう。
「無駄に関数を public にするなよ!」
採用情報
クラウドクリエイティブスタジオではエンジニアの方を絶賛募集中です。
一緒に面白いゲームを作っていきましょう!