ASP.NET 웹 API에서 여러 GET 메소드가있는 단일 컨트롤러


167

웹 API에서는 비슷한 구조의 클래스가 있습니다.

public class SomeController : ApiController
{
    [WebGet(UriTemplate = "{itemSource}/Items")]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebGet(UriTemplate = "{itemSource}/Items/{parent}")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

개별 방법을 매핑 할 수 있으므로 적절한 장소에서 올바른 요청을 얻는 것이 매우 간단했습니다. 단일 GET메소드 만 가지고 있지만 Object매개 변수 가있는 유사한 클래스의 경우 성공적으로 사용했습니다 IActionValueBinder. 그러나 위에서 설명한 경우 다음과 같은 오류가 발생합니다.

Multiple actions were found that match the request: 

SomeValue GetItems(CustomParam parameter) on type SomeType

SomeValue GetChildItems(CustomParam parameter, SomeObject parent) on type SomeType

지금까지의 ExecuteAsync방법을 재정 의하여이 문제에 접근하려고 ApiController하지만 운이 없습니다. 이 문제에 대한 조언이 있습니까?

편집 : 라우팅에 다른 접근 방식을 가진 ASP.NET 웹 API 에서이 코드를 이동하려고한다고 언급하는 것을 잊었습니다. 문제는 ASP.NET 웹 API에서 코드가 작동하게하려면 어떻게해야합니까?


1
여전히 {parent}를 RouteParameter.Optional으로 가지고 있습니까?
Antony Scott

그래, 내가 했어. 어쩌면 IActionValueBinder를 잘못된 방법으로 사용하고 있습니다 (모범에서와 같이 int id와 같은 유형의 경우 제대로 작동하기 때문입니다).
paulius_l 2019

죄송합니다. 더 명확해야했습니다. 선택 사항으로 사용하면 항목 경로 및 하위 항목 경로와 일치한다는 것을 의미한다고 생각했을 것입니다. 이는 오류 메시지를 설명합니다.
Antony Scott

아래의 접근 방식이 여러 경로로 적절한 REST 규칙에 위배되는 경우 현재 논의 중입니까? 제 생각에는 이것이 좋습니다. 내 동료는 그것이 좋지 않다고 생각합니다. 이것에 대한 의견이 있으십니까?
Remy

REST에 대해 읽기 시작할 때 일반적으로 반대했습니다. 나는 그것이 적절한 접근 방법인지 확실하지 않지만 때로는 더 편리하거나 사용하기 쉽기 때문에 규칙을 약간 구부리는 것은 그렇게 나쁘지 않을 수 있습니다. 특정 문제를 해결하기 위해 노력하는 한. 이 질문을 게시 한 지 6 개월이 지났으며 그 이후로이 방법을 사용한 후회가 없었습니다.
paulius_l

답변:


249

이것이 추가 GET 메소드를 지원하고 일반 REST 메소드도 지원하는 가장 좋은 방법입니다. WebApiConfig에 다음 경로를 추가하십시오.

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

아래 테스트 클래스 로이 솔루션을 확인했습니다. 아래 컨트롤러에서 각 방법을 성공적으로 수행 할 수있었습니다.

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

다음 요청을 지원하는지 확인했습니다.

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

참고 하여 추가 GET 작업이 시작되지 않는 경우이 방법에 HttpGet 속성을 추가 할 수 있습니다 '가져 오기'고.


4
이것은 훌륭한 답변이며 다른 관련 질문으로 많은 도움이되었습니다. 감사!!
Alfero Chingono

4
이것을 시도-작동하지 않는 것 같습니다. 경로는 모두 무작위로 GetBlah (long id) 메서드에 매핑됩니다. :(
BrainSlugs83 2013 년

1
@ BrainSlugs83 : 주문에 따라 다릅니다. 그리고 당신은 "withId"메소드에 추가하고 싶을 것입니다.constraints: new{id=@"\d+"}
Eric Falsken

4
Get (int id, string name) 메서드를 하나 더 추가하는 방법은 무엇입니까? ... 실패
Anil Purswani

1
나는이 같은 여분의 경로를 추가했다 routes.MapHttpRoute("DefaultApiPut", "Api/{controller}", new {action = "Put"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Put)});나의를 위해 Put그렇지 않으면 나에게 (404)주고 있었다 방법
에드 알리 Taqi

57

이것에서 가십시오 :

config.Routes.MapHttpRoute("API Default", "api/{controller}/{id}",
            new { id = RouteParameter.Optional });

이에:

config.Routes.MapHttpRoute("API Default", "api/{controller}/{action}/{id}",
            new { id = RouteParameter.Optional });

따라서 이제 HTTP 요청을 보낼 작업 (방법)을 지정할 수 있습니다.

에 게시 에 "http : // localhost를 : 8383 / API / 명령 / PostCreateUser" 호출합니다

public bool PostCreateUser(CreateUserCommand command)
{
    //* ... *//
    return true;
}

및 게시에 ": // localhost를 8383 / API / 명령 / PostMakeBooking HTTP" 호출합니다

public bool PostMakeBooking(MakeBookingCommand command)
{
    //* ... *//
    return true;
}

나는 자체 호스팅 WEB API 서비스 응용 프로그램에서 이것을 시도했고 그것은 매력처럼 작동합니다 :)


8
유용한 답변에 감사드립니다. Get, Post 등으로 메소드 이름을 시작하면 요청이 사용 된 HTTP 동사를 기반으로 해당 메소드에 맵핑됩니다. 그러나 메소드 이름을 지정 [HttpGet]하고 [HttpPost], 등 속성으로 메소드를 장식 하여 동사를 메소드에 맵핑 할 수도 있습니다.
indot_brad

친절하게 내 질문을
Moeez

@DikaArtaKarunia 문제 없음, 내 대답이 6 년 후에도 여전히 적용 가능하다는 것을 기쁘게 생각합니다 : D
uggeh

31

코드를 통해 수동으로 추가하는 것보다 사용하기 쉬운 속성을 찾습니다. 다음은 간단한 예입니다.

[RoutePrefix("api/example")]
public class ExampleController : ApiController
{
    [HttpGet]
    [Route("get1/{param1}")] //   /api/example/get1/1?param2=4
    public IHttpActionResult Get(int param1, int param2)
    {
        Object example = null;
        return Ok(example);
    }

}

webapiconfig에서도 필요합니다.

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

좋은 링크들 http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api 라우팅을 더 잘 설명합니다. http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api


3
나는 또한 추가 할 필요 config.MapHttpAttributeRoutes();내에 WebApiConfig.cs, 그리고 GlobalConfiguration.Configuration.EnsureInitialized();내 말에 WebApiApplication.Application_Start()방법 일 행 노선의 특성을 얻을 수 있습니다.
Ergwun

@ Ergwun이 의견은 저에게 많은 도움이되었습니다. 여기에 추가 config.MapHttpAttributeRoutes();하려면 경로 매핑 전에 나타나야합니다 (예 : config.Routes.MappHttpRoute(....
Philip Stratford

11

global.asax.cs에서 다음과 같이 추가 경로를 정의해야합니다.

routes.MapHttpRoute(
    name: "Api with action",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

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

5
그렇습니다. 그러나 실제로 이러한 경로의 예를 보는 것이 좋습니다. 이 답변은 지역 사회에 더 가치있는 것입니다. (당신은 :) 내에서 +1을받을 것
아란 멀홀랜드에게

여기 예를 읽을 수 있습니다 - stackoverflow.com/questions/11407267/...
톰 Kerkhove

2
실제 솔루션이 더 좋았을 것입니다.
너무 많은 고블린

6

최신 Web Api 2에서는 여러 가지 get 메소드를 사용하는 것이 더 쉬워졌습니다.

GET메소드에 전달 된 매개 변수 가 속성 라우팅 시스템이 ints 및 Guids 의 경우와 같이 유형을 구별하기에 충분히 다른 경우 [Route...]속성에 예상되는 유형을 지정할 수 있습니다

예를 들어-

[RoutePrefix("api/values")]
public class ValuesController : ApiController
{

    // GET api/values/7
    [Route("{id:int}")]
    public string Get(int id)
    {
       return $"You entered an int - {id}";
    }

    // GET api/values/AAC1FB7B-978B-4C39-A90D-271A031BFE5D
    [Route("{id:Guid}")]
    public string Get(Guid id)
    {
       return $"You entered a GUID - {id}";
    }
} 

이 방법에 대한 자세한 내용은 여기를 참조하십시오 http://nodogmablog.bryanhogan.net/2017/02/web-api-2-controller-with-multiple-get-methods-part-2/

다른 옵션은 GET방법에 다른 경로 를 제공하는 것 입니다.

    [RoutePrefix("api/values")]
    public class ValuesController : ApiController
    {
        public string Get()
        {
            return "simple get";
        }

        [Route("geta")]
        public string GetA()
        {
            return "A";
        }

        [Route("getb")]
        public string GetB()
        {
            return "B";
        }
   }

자세한 내용은 여기를 참조하십시오 -http://nodogmablog.bryanhogan.net/2016/10/web-api-2-controller-with-multiple-get-methods/


5

ASP.NET Core 2.0에서는 경로 속성을 컨트롤러에 추가 할 수 있습니다 .

[Route("api/[controller]/[action]")]
public class SomeController : Controller
{
    public SomeValue GetItems(CustomParam parameter) { ... }

    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

4

여러 Get 메소드를 허용하기 위해 Web Api 2 속성 라우팅을 사용하려고 시도했지만 이전 답변의 유용한 제안을 통합했지만 Controller에서는 "특별한"메소드 만 장식했습니다 (예).

[Route( "special/{id}" )]
public IHttpActionResult GetSomethingSpecial( string id ) {

... 또한 컨트롤러 상단에 [RoutePrefix]를 배치하지 않아도됩니다 :

[RoutePrefix("api/values")]
public class ValuesController : ApiController

제출 된 URI와 일치하는 경로를 찾을 수 없다는 오류가 발생했습니다. 일단 [Route] 메소드를 꾸미고 Controller 전체를 [RoutePrefix] 꾸미기를하면 효과가있었습니다.


3

u가 답을 찾았는지 확실하지 않지만 이것을 수행하면 작동합니다.

public IEnumerable<string> Get()
{
    return new string[] { "value1", "value2" };
}

// GET /api/values/5
public string Get(int id)
{
    return "value";
}

// GET /api/values/5
[HttpGet]
public string GetByFamily()
{
    return "Family value";
}

이제 global.asx에서

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

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

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

routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

3

WebInvokeAttribute로 전환하고 메소드를 "GET"으로 설정하려고 했습니까?

나는 비슷한 문제가 있다고 생각하고 모든 방법이 아닌 대부분의 방법에서 어떤 방법 (GET / PUT / POST / DELETE)이 예상되는지 명시 적으로 알려주는 것으로 전환했습니다.

public class SomeController : ApiController
{
    [WebInvoke(UriTemplate = "{itemSource}/Items"), Method="GET"]
    public SomeValue GetItems(CustomParam parameter) { ... }

    [WebInvoke(UriTemplate = "{itemSource}/Items/{parent}", Method = "GET")]
    public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
}

WebGet 처리해야하지만 동일한 반환 유형의 다중 Get 훨씬 적은 다중 Get과 관련된 문제가 있음을 알았습니다.

[편집 : WCF WebAPI의 일몰과 MVC 스택에서 ASP.Net WebAPI 로의 마이그레이션에는이 중 어느 것도 유효하지 않습니다]


1
죄송합니다. WCF 웹 API가 중단 된 이후 코드를 ASP.NET 웹 API로 옮기는 것을 언급하지 않았습니다. 게시물을 수정했습니다. 감사합니다.
paulius_l

2
**Add Route function to direct the routine what you want**
    public class SomeController : ApiController
    {
        [HttpGet()]
        [Route("GetItems")]
        public SomeValue GetItems(CustomParam parameter) { ... }

        [HttpGet()]
        [Route("GetChildItems")]
        public SomeValue GetChildItems(CustomParam parameter, SomeObject parent) { ... }
    }

스택 오버플로에 오신 것을 환영합니다! 제발 답변을 편집 코드에 대한 설명뿐만 아니라, 그것을 여기 열네 다른 답변과 다른 방법에 대한 설명을 포함 할 수 있습니다. 이 질문은 거의 8 살 이며 이미 수용되고 잘 설명 된 답변이 여러 개 있습니다. 에 대한 설명없이 당신 , 그것은 가능성을 downvoted 또는 제거 얻을 것이다. 그 설명이 있으면이 질문에 대한 당신의 대답의 위치를 ​​정당화하는 데 도움이 될 것입니다.
Das_Geek

1
이 명확하고 기본적인 질문에 대해 개인적으로 (SO 권장 사항이 무엇인지 알고 있습니다.) 개인적으로 순수한 코드 답변을 원할 것 입니다. 유용한 기능성 소프트웨어를 빠르게 만들고자하는 많은 설명을 읽고 싶지 않습니다 . +1
MemeDeveloper

2

게으른 / 서둘러 대안 (도넷 코어 2.2) :

[HttpGet("method1-{item}")]
public string Method1(var item) { 
return "hello" + item;}

[HttpGet("method2-{item}")]
public string Method2(var item) { 
return "world" + item;}

그들에게 전화 :

로컬 호스트 : 5000 / api / controllername / method1-42

"hello42"

로컬 호스트 : 5000 / api / controllername / method2-99

"world99"


0

위의 예 중 어느 것도 나의 개인적인 요구에 효과가 없었습니다. 아래는 내가 한 일입니다.

 public class ContainsConstraint : IHttpRouteConstraint
{       
    public string[] array { get; set; }
    public bool match { get; set; }

    /// <summary>
    /// Check if param contains any of values listed in array.
    /// </summary>
    /// <param name="param">The param to test.</param>
    /// <param name="array">The items to compare against.</param>
    /// <param name="match">Whether we are matching or NOT matching.</param>
    public ContainsConstraint(string[] array, bool match)
    {

        this.array = array;
        this.match = match;
    }

    public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        if (values == null) // shouldn't ever hit this.                   
            return true;

        if (!values.ContainsKey(parameterName)) // make sure the parameter is there.
            return true;

        if (string.IsNullOrEmpty(values[parameterName].ToString())) // if the param key is empty in this case "action" add the method so it doesn't hit other methods like "GetStatus"
            values[parameterName] = request.Method.ToString();

        bool contains = array.Contains(values[parameterName]); // this is an extension but all we are doing here is check if string array contains value you can create exten like this or use LINQ or whatever u like.

        if (contains == match) // checking if we want it to match or we don't want it to match
            return true;
        return false;             

    }

경로에서 위를 사용하려면 다음을 사용하십시오.

config.Routes.MapHttpRoute("Default", "{controller}/{action}/{id}", new { action = RouteParameter.Optional, id = RouteParameter.Optional}, new { action = new ContainsConstraint( new string[] { "GET", "PUT", "DELETE", "POST" }, true) });

이 라우트는 기본 GET, POST, PUT 및 DELETE 메소드와 만 일치하도록 메소드에서 제한 유형의 가짜가 발생합니다. "true"는 배열의 항목이 일치하는지 확인하고 싶다고 말합니다. 그것이 거짓이라면 str에서 경로를 제외한다고 말하고 다음과 같이이 기본 방법 위의 경로를 사용할 수 있습니다.

config.Routes.MapHttpRoute("GetStatus", "{controller}/status/{status}", new { action = "GetStatus" });

위의 내용은 본질적으로 다음 URL => http://www.domain.com/Account/Status/Active또는 이와 유사한 것을 찾습니다 .

위를 넘어서서 너무 미쳤을 지 모르겠다. 하루가 끝나면 리소스마다 있어야합니다. 그러나 여러 가지 이유로 친숙한 URL을 매핑해야 할 필요가 있습니다. Web Api가 진화함에 따라 일종의 프로비저닝이있을 것이라고 확신합니다. 시간이 지나면 더 영구적 인 솔루션을 구축하고 게시하겠습니다.


new System.Web.Http.Routing.HttpMethodConstraint(HttpMethod.Get, HttpMethod.Post, HttpMethod.Put, HttpMethod.Delete) 대신 사용할 수 있습니다 .
abatishchev

0

위의 라우팅 솔루션을 작동시킬 수 없었습니다. 일부 구문이 변경 된 것으로 보이며 여전히 MVC를 처음 사용합니다. 지금까지 이것은 "public MyObject GetMyObjects (long id)"메소드를 대체합니다. "id"의 유형을 문자열로 변경하고 반환 유형을 object로 변경합니다.

// GET api/MyObjects/5
// GET api/MyObjects/function
public object GetMyObjects(string id)
{
    id = (id ?? "").Trim();

    // Check to see if "id" is equal to a "command" we support
    // and return alternate data.

    if (string.Equals(id, "count", StringComparison.OrdinalIgnoreCase))
    {
        return db.MyObjects.LongCount();
    }

    // We now return you back to your regularly scheduled
    // web service handler (more or less)

    var myObject = db.MyObjects.Find(long.Parse(id));
    if (myObject == null)
    {
        throw new HttpResponseException
        (
            Request.CreateResponse(HttpStatusCode.NotFound)
        );
    }

    return myObject;
}

0

동일한 파일 내에 여러 액션이있는 경우 동일한 액션 (예 : ID)을 모든 액션에 전달하십시오. 액션은 ID 만 식별 할 수 있기 때문에 인수에 이름을 지정하는 대신 ID 만 선언하면됩니다.


[httpget]
[ActionName("firstAction")] firstAction(string Id)
{.....
.....
}
[httpget]
[ActionName("secondAction")] secondAction(Int Id)
{.....
.....
}
//Now go to webroute.config file under App-start folder and add following
routes.MapHttpRoute(
name: "firstAction",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

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

Url은 브라우저에서 각 기능을 어떻게 보입니까?
Si8

0

간단한 대안

쿼리 문자열을 사용하십시오.

라우팅

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

제어 장치

public class TestController : ApiController
{
    public IEnumerable<SomeViewModel> Get()
    {
    }

    public SomeViewModel GetById(int objectId)
    {
    }
}

요청

GET /Test
GET /Test?objectId=1

노트

쿼리 문자열 param은 "id"이거나 매개 변수가 구성된 경로에 있으면 안됩니다.


-1

WebApiConfig를 수정하고 다음 과 같이 다른 Routes.MapHttpRoute를 끝에 추가하십시오.

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

그런 다음 다음과 같이 컨트롤러를 만듭니다.

public class ServiceController : ApiController
{
        [HttpGet]
        public string Get(int id)
        {
            return "object of id id";
        }
        [HttpGet]
        public IQueryable<DropDownModel> DropDowEmpresa()
        {
            return db.Empresa.Where(x => x.Activo == true).Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public IQueryable<DropDownModel> DropDowTipoContacto()
        {
            return db.TipoContacto.Select(y =>
                  new DropDownModel
                  {
                      Id = y.Id,
                      Value = y.Nombre,
                  });
        }

        [HttpGet]
        public string FindProductsByName()
        {
            return "FindProductsByName";
        }
}

이것이 내가 해결 한 방법입니다. 누군가에게 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.