4

스택 오버플로에 대해 동일한 질문을하는 사람이 천 명이 넘는 것으로 보이지만이 문제에 대한 해결책은 하나도 없습니다. 나는 ... 다시 물어 갈거야 여러 Get 액션이있을 때의 MVC API 라우팅

I가 다음 작업이있는 API 컨트롤러 : 나는 정의 내 APIConfig에서

// 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; 
    } 

다음과 같은 경로 :

 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+" } 
     ); 

I을 다음 작업에 아무런 문제없이 액세스 할 수 있습니다. /api/exploitation /api/exploitation/getbysongwriterid/1 /api/exploitation/getbysongid/1

내가 액세스/API/착취하려고하면

는/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. 

사람이 내 경로에 어떤 문제가 있는지 볼 수 있을까요? 아니면 다른 무엇인가 잘못 되었습니까?

답변

6

나는이 문제에 대한 우아한 해결책을 발견했다.지금은 액세스 할 수 있습니다

 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 } 
     ); 

:

나는 다음과 같은 경로를 가지고 내 ApiRouteConfig을 수정

/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) } 
    ); 

나는이 사람들이 겪고있는 공통적 인 문제가 될 것 같은이 대답은 모든 사람을 도움이 희망을.

+0

이것은 거의 완벽합니다. ID가 GUID 일 필요가있을 때 어떤 일이 발생합니까? – rooter

0

당신이 가진 문제는 /API/착취/1가에 해당한다는 것입니다 :뿐만 아니라 라우팅하는 것은 특히 때문에 {ID가}는 선택 사항이며 자신의 컨트롤러 만족하는 것으로 귀하의 모든 GET 방식의

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

는 똑같은.

그래서 클라이언트에서 하나의 HTTP GET 요청과 GET 요청을 수락하는 여러 가지 방법이 있습니다. 어느 사람에게 갈지는 모른다.

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

귀하의 질문에 대한 답변입니다.

+0

응답 해 주셔서 감사합니다. 그러나 실제로 내 질문에는 답변이 없으므로 이것이 작동하지 않는 이유를 알려줍니다. 이 작품을 만드는 방법에 대한 아이디어가 있습니까? 나는이게 가능하다고 생각하지 않습니까? – mcottingham

0

경로 정의에서 다음을 시도해보십시오. 그 ID가 기본값이되도록

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

은 첫 번째 가져 오기 방법은 비공개로 두 번째를 수정 :은 다음의 경로를 유지

이 방법은 (내가 나 자신을 테스트하지 않았습니다)
// 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/착취/API/착취에 매핑합니다/ID로 (기본 동작으로) 가져 오기 = 0 기본 PARAM로
  • API/착취/1 줘야

    API/착취에 L 맵// 1 그래서 (1)
  • API/착취/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+" } 
    ); 

하지만 뭔가 그것을 내가이 문제를 해결 희망을 시도해 : 타이트한 경로 정의는 실제로 이렇게 될 수 있습니다.

+0

답장을 보내 주셔서 감사합니다. 그러나 다른 사람에게 다른 사람을 불러야하는 태도가 얼마나 유쾌하지 않은지에 대해 저는 정말로 행복하지 않습니다. 나는 매우 우아하고 지금 그것을 게시 할 해결책이 있습니다. – mcottingham

+0

전혀 문제 없습니다. 궁금해서 반갑습니다. 단지 뭔가를 명확히하기 위해서 : 내가 올린 해결책은 행동에서 행동을 부르지 않는다. 첫 번째 방법이 비공개로 전환되면 더 이상 행동이 아닙니다. –