1

WebAPI (最初のもの) で API を開発していますが、ルートの特異性と、ネストされたリソースのコントローラーのアクションへのマッピングに問題があります。

次のリソースがあります。

  1. プロジェクト (ルート レベルのリソース)
  2. サイト (ルート レベルのリソース)
  3. Zones (サイトのサブリソース)

だから私はいくつかのファンキーな入れ子を持つことができます:

  • /api/projects/1/sites
  • /api/projects/1/sites/assigned
  • /api/sites/1/zones
  • /api/sites/1/zones/1

私が抱えている問題は、いくつかのエンド ポイント、特にサイト/ゾーンのものにあります。現在、2 つの Get メソッドがあります。1 つは siteId とページでゾーンを取得するメソッドで、もう 1 つは Id でゾーンを取得するメソッドです (以下のコードは面倒です。リファクタリングが必要ですが、WebAPI に頭を悩ませようとしています)。

    public HttpResponseMessage Get(int id)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.OK);

        var zone = (from z in _repo
                    where z.Id == id
                    select z)
            .SingleOrDefault()
            .ToRepresentation<Zone, ZoneRepresentation>();

        resp.Content = new ObjectContent<ZoneRepresentation>(zone, new JsonHalMediaTypeFormatter());

        return resp;
    }

    public HttpResponseMessage GetByPage(int siteId, int page = DefaultPage, int pageSize = PageSize)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.OK);

        //TODO: Make sure access to project is checked....

        var pagingInfo = (from z in _repo
                          where z.Site.Id == siteId
                          select z)
                          .GetPagingInfo<Zone>(pageSize);

        if (pagingInfo.TotalRecords > 0)
        {
            if (page > pagingInfo.TotalPages)
            {
                resp.StatusCode = HttpStatusCode.NotFound;
                throw new HttpResponseException(resp);
            }

            var zones = (from z in _repo.Include("Site")
                         where z.Site.Id == siteId
                         select z)
                         .OrderBy(z => z.Name)
                         .Paging(page, DefaultPage, pageSize)
                         .ToPagedRepresentation<Zone, ZoneListRepresentation>(pagingInfo.TotalRecords, pagingInfo.TotalPages, page, new Link("Zones", "/sites/" + siteId.ToString() + "/zones?page={page}"));

            resp.Content = new ObjectContent<ZoneListRepresentation>(zones, new JsonHalMediaTypeFormatter());
        }
        else
        {
            resp.StatusCode = HttpStatusCode.NoContent;
            throw new HttpResponseException(resp);
        }

        return resp;
    }

追加のルートが指定されていないと、これらは機能しないため、アクセスしようとしています:

  • /api/sites/1/zones
  • /api/sites/1/zones/1

404 になります。

最初のルートを機能させるために、次のルート構成を追加します (より一般的なアプローチがあると確信していますが、これをできるだけ簡単に機能させたいだけです)。

        config.Routes.MapHttpRoute(
            name: "SiteZonesApiRoute",
            routeTemplate: "api/sites/{siteId}/zones/{id}",
            defaults: new { Controller = "Zones", id = RouteParameter.Optional }
        );

これは最初のリンクで機能しますが、取得した ID を渡します。

ExceptionMessage":"Multiple actions were found that match the request:     
System.Net.Http.HttpResponseMessage Get(Int32) on type API.Controllers.ZonesController
System.Net.Http.HttpResponseMessage GetByPage(Int32, Int32, Int32) on type 
API.Controllers.ZonesController

1つは1つのintと他の3つを受け入れますが、私は2つのGETメソッドを持っています. 次に、ID タイプの非常に具体的なルート ハンドラを追加し、特定のアクション メソッドを指すようにします。

        config.Routes.MapHttpRoute(
             name: "ZoneByIdApiRoute",
             routeTemplate: "api/sites/{siteId}/zones/{id}",
             defaults: new { Controller = "Zones", action="Get" }
         );

素晴らしい、それはうまくいきました......まあ、なんらかの理由で、非常に特定のルートが PUT メソッドを完全に壊し、常に 405 を返し、それを回避できません。

ここで私が間違ったことをしたことは明らかですか?さらにコントローラーを作成することもできますが、それは問題を回避しているようで、完全に立ち往生しています。

4

1 に答える 1

0

コントローラー メソッドのシグネチャがルートと一致する場合、動作するはずです。

次のルートが一致するように記述します。

  • /api/sites/1/zones
  • /api/sites/1/zones/1

つまり、siteId は必須パラメーターであり、id (zoneId) はオプションです。

これは、siteId と id をパラメーターとして受け取る Get または GetSomething という名前のコントローラー メソッドが必要であることを意味します。Id はオプションの場合もあれば、パラメータとして siteId のみを持つ別のメソッドを使用することもできます。

たとえば、これは機能します:

    public class ZonesController : ApiController
    {
    // /api/sites/1/zones/1
    public HttpResponseMessage Get(int siteId, int id)
    {
        return Request.CreateResponse<string>(HttpStatusCode.OK, string.Format("siteId: {0}. Id: {1}", siteId, id));
    }

    // /api/sites/1/zones?page=12&pageSize=30
    public HttpResponseMessage GetByPage(int siteId, int page = 0, int pageSize = 10)
    {
        return Request.CreateResponse<string>(HttpStatusCode.OK, string.Format("siteId: {0}. page: {1}", siteId, page));
    }
}

ルート構成:

config.Routes.MapHttpRoute(
        name: "SiteZonesApiRoute",
        routeTemplate: "api/sites/{siteId}/zones/{id}",
        defaults: new { Controller = "Zones", id = RouteParameter.Optional }
    );
于 2013-08-13T20:19:56.297 に答える