LoginSignup
0
0

More than 3 years have passed since last update.

ASP.NET WebAPIで「同一URL・HTTPメソッド違い」のControllerが実行時エラーとなる場合の対処法

Posted at

ASP.NET WebAPIで「同一URL・HTTPメソッド違い」のControllerが実行時エラーとなる場合の対処法

ASP.NETのWebAPI Frameworkでは、Route Attributeを用いて、エンドポイントマッピングができる機能がある。
しかし同一エンドポイントで複数APIを実装しようとした際にハマったのでメモ。

前提

・できるだけ「1つのコントロールクラスに1つのアクションメソッド」としたい。
・メンバー情報の取得(GET)、更新(PUT)という2つの機能を実装する

NGケース

上記前提のもと、下記のように実装した場合…

MemberFindController.cs
using System.Web.Http;

namespace ExampleWebApi.Controller
{
    /// <summary>
    /// メンバー情報取得コントローラ
    /// </summary>
    public class MemberFindController : ApiController
    {
        /// <summary>
        /// メンバー情報取得コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        /// <returns>メンバー情報文字列</returns>
        [Route("api/me/{id}")]
        [HttpGet]
        public string MemberFind(int id)
        {
            // 何らかの取得処理の呼び出し
            return "ExampleMemberInfo";
        }
    }
}
MemberUpdateController.cs
using System.Web.Http;

namespace ExampleWebApi.Controller
{
    /// <summary>
    /// メンバー情報更新コントローラ
    /// </summary>
    public class MemberUpdateController : ApiController
    {
        /// <summary>
        /// メンバー情報更新コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        [Route("api/me/{id}")]
        [HttpPut]
        public void MemberUpdate(int id, [FromBody] string value)
        {
            // 更新処理の呼び出し
        }
    }
}

HTTPメソッド定義で一意となっているにもかかわらず、Controllerクラスが特定できない旨の実行時エラーとなってしまう。

/api/me/1
{
    "Message": "エラーが発生しました。",
    "ExceptionMessage": "URL に一致する複数のコントローラー型が見つかりました。これは、複数のコントローラー型の属性ルートが要求された URL に一致する場合に発生する可能性があります。¥r¥n¥r¥n要求によって、次の一致するコントローラー型が見つかりました: ¥r¥nAilsApi.Controllers.MemberUpdateController¥r¥nAilsApi.Controllers.MemberFindController",
    "ExceptionType": "System.InvalidOperationException",
    "StackTrace": "   場所 System.Web.Http.Dispatcher.DefaultHttpControllerSelector.GetDirectRouteController(IHttpRouteData routeData)¥r¥n   場所 System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(HttpRequestMessage request)¥r¥n   場所 System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"
}

OKケース

策としては、エンドポイントを変える、または諦めて以下のように同一クラス内に記述すること。

MemberController.cs
using System.Web.Http;

namespace ExampleWebApi.Controller
{
    /// <summary>
    /// メンバー情報操作コントローラ
    /// </summary>
    public class MemberController : ApiController
    {
        /// <summary>
        /// メンバー情報取得コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        /// <returns>メンバー情報文字列</returns>
        [Route("api/me/{id}")]
        [HttpGet]
        public string MemberFind(int id)
        {
            // 何らかの取得処理の呼び出し

            return "ExampleMemberInfo";
        }

        /// <summary>
        /// メンバー情報更新コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        [Route("api/me/{id}")]
        [HttpPut]
        public void MemberUpdate(int id, [FromBody] string value)
        {
            // 更新処理の呼び出し
        }
    }
}

このような挙動を示す理由(予想)

上述の謎な挙動ではあるが、全く理由が思い至らないわけでもない。
例として、同じロジックを書くにも以下のような書き方もある。

MeController.cs
using System.Web.Http;

namespace ExampleWebApi.Controller
{
    /// <summary>
    /// メンバー情報操作コントローラ
    /// </summary>
    public class MeController : ApiController
    {
        /// <summary>
        /// メンバー情報取得コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        /// <returns>メンバー情報文字列</returns>
        public string Get(int id)
        {
            // 何らかの取得処理の呼び出し

            return "ExampleMemberInfo";
        }

        /// <summary>
        /// メンバー情報更新コントローラ
        /// </summary>
        /// <param name="id">メンバーID</param>
        public void Put(int id, [FromBody] string value)
        {
            // 更新処理の呼び出し
        }
    }
}

Controllerクラス名でルーティングを、アクションメソッド名でHTTPメソッドを表現する記法である。
この記法だと当然「HTTPメソッド違いは同一クラス内に」という制限が発生するわけだが、この制限のみがAttributeを使用したルーティングにも何故か適用されているものと考えられる。
Attributeによる柔軟なルーティング指定を実現させているはずなのに、同一クラスの制限をもたせる必要性はないと思われるのだが… .NETは奥深い。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0