4

スタック オーバーフローについて同じ質問をする人が何千人もいるようですが、この問題に対する単一の解決策はないようです。もう一度聞いてみます...

次のアクションを持つ API コントローラーがあります。

    // GET api/Exploitation
    public HttpResponseMessage Get() {
        var items = _exploitationRepository.FindAll();

        var mappedItems = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(items);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItems);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { }));
        return response;
    }

    // GET api/Exploitation/5        
    [HttpGet, ActionName("Get")]
    public HttpResponseMessage Get(int id) {
        var item = _exploitationRepository.FindById(id);
        var mappedItem = Mapper.Map<Exploitation, ExploitationView>(item);

        var response = Request.CreateResponse<ExploitationView>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

    // GET api/Exploitation/GetBySongwriterId/5
    [HttpGet, ActionName("GetBySongwriterId")]
    public HttpResponseMessage GetBySongwriterId(int id) {
        var item = _exploitationRepository.Find(e => e.Song.SongWriterSongs.Any(s => s.SongWriterId == id))
                                          .OrderByDescending(e => e.ReleaseDate);
        var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

    // GET api/Exploitation/GetBySongwriterId/5
    [HttpGet, ActionName("GetBySongId")]
    public HttpResponseMessage GetBySongId(int id) {
        var item = _exploitationRepository.Find(e => e.SongId == id)
                                          .OrderByDescending(e => e.ReleaseDate);
        var mappedItem = Mapper.Map<IEnumerable<Exploitation>, IEnumerable<ExploitationView>>(item);

        var response = Request.CreateResponse<IEnumerable<ExploitationView>>(HttpStatusCode.OK, mappedItem);
        response.Headers.Location = new Uri(Url.Link("DefaultApi", new { id = id }));
        return response;
    }

APIConfig で、次のルートを定義しました。

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        config.Routes.MapHttpRoute(
            name: "ActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional },
            constraints: new { id = @"\d+" }
        );

次のアクションに問題なくアクセスできることがわかりました: /api/exploitation /api/exploitation/getbysongwriterid/1 /api/exploitation/getbysongid/1

/api/exploitation/1 にアクセスしようとすると、この例外が発生します

"Multiple actions were found that match the request: System.Net.Http.HttpResponseMessage Get(Int32) on type Songistry.API.ExploitationController System.Net.Http.HttpResponseMessage GetBySongwriterId(Int32)" exception.

私のルートの何が問題なのか誰にもわかりますか? それとも他の何かが間違っていますか?

4

3 に答える 3

6

問題に対するエレガントな解決策を見つけました。

次のルートを持つように ApiRouteConfig を変更しました。

        config.Routes.MapHttpRoute(
            name: "DefaultGetApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional, action = "Get" },
            constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Get) }
        );

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: new { id = @"\d+" }
        );            

        config.Routes.MapHttpRoute(
            name: "ActionApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional, action = RouteParameter.Optional }
        );

今私はアクセスできます:

/api/exploitation
/api/exploitation/1
/api/exploitation/getbysongid/1
/api/exploitation/getbysongwriterid/1

この新しいルーティング構成で動作するようにコントローラー アクションを変更する必要はまったくありませんでした。

複数の PUT または POST アクションがある場合、次のような新しいルートを作成できます。

    config.Routes.MapHttpRoute(
        name: "DefaultGetApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Put" },
        constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Put) }
    );

    config.Routes.MapHttpRoute(
        name: "DefaultGetApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Delete" },
        constraints: new { id = @"\d+", httpMethod = new HttpMethodConstraint(HttpMethod.Delete) }
    );

これは人々が抱えている一般的な問題のように思われるため、この回答がすべての人に役立つことを願っています.

于 2013-03-09T17:56:04.090 に答える
0

ルート定義で次のことを試してください。次のルートのみを保持します。

  config.Routes.MapHttpRoute(
        name: "ActionApi",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Get" },
        constraints: new { id = @"\d+" }
    );

最初の Get メソッドを非公開にし、id がデフォルト値を持つように 2 番目のメソッドを変更します。

// GET api/Exploitation
private HttpResponseMessage Get() {
    // implementation stays the same but now it's private
}

// GET api/Exploitation/5        
[HttpGet, ActionName("Get")]
public HttpResponseMessage Get(int id = 0) {
    if (id == 0) {
        return Get();
    }

    // continue standard implementation
}

このように(私は自分でテストしていません)、次のことを期待しています:

  • api/Exploitation/ は api/Exploitation/Get (デフォルト アクションとして) にマップされ、id = 0 がデフォルト パラメータとして設定されます。
  • api/Exploitation/1 は api/Exploitation/Get/1 にマップされるため、Get(1) が呼び出されます。
  • api/Exploitation/someOtherAction/345 は正しいアクション メソッドを呼び出します

これはうまくいくかもしれません。より厳密なルート定義は、実際には次のようになります。

  config.Routes.MapHttpRoute(
        name: "ApiWithRequiredId",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: null /* make sure we have explicit action and id */,
        constraints: new { id = @"\d+" }
    );

  config.Routes.MapHttpRoute(
        name: "ApiWithOptionalId",
        routeTemplate: "api/{controller}/{action}/{id}",
        defaults: new { id = RouteParameter.Optional, action = "Get" },
        constraints: new { action = "Get" /* only allow Get method to work with an optional id */, id = @"\d+" }
    );

しかし、これらの線に沿った何か...試してみてください。問題が解決することを願っています。

于 2013-03-09T13:28:18.113 に答える
0

あなたが抱えている問題は、/api/exploitation/1が以下に該当することです:

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
    );

特に{id}はオプションであり、それらのコントローラーは同じであるため、すべての GET メソッドがそのルーティングも満たしています。

したがって、クライアントからの 1 つの HTTP GET 要求と、GET 要求を受け入れる複数のメソッドがあります。どちらに行くべきかはわかりません。

api/{controller}/{action}/{id} 
//This works fine because you specified which action explicitly

それがあなたの質問に答えることを願っています。

于 2013-03-09T06:42:48.940 に答える