웹 API 컨트롤러의 다중 HttpPost 메소드


126

MVC4 Web API 프로젝트를 사용하기 시작했으며 여러 HttpPost방법 이있는 컨트롤러가 있습니다. 컨트롤러는 다음과 같습니다.

제어 장치

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

다음 MyRequestTemplate은 요청을 통해 오는 Json을 처리하는 템플릿 클래스를 나타냅니다.

오류:

Fiddler를 사용하여 요청 http://localhost:52370/api/VTRouting/TSPRoute하거나 http://localhost:52370/api/VTRouting/Route 오류가 발생하면 :

요청과 일치하는 여러 조치가 발견되었습니다.

위의 방법 중 하나를 제거하면 정상적으로 작동합니다.

Global.asax

에서 기본 라우팅 테이블을 수정하려고 시도했지만 global.asax여전히 오류가 발생합니다. global.asax에서 경로를 정의하는 데 문제가 있다고 생각합니다. 다음은 global.asax에서하는 일입니다.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

POST를 사용하여 Fiddler에서 요청을하고 MyRequestTemplate의 RequestBody에서 json을 전달합니다.

답변:


143

단일 컨트롤러에서 여러 작업을 수행 할 수 있습니다.

이를 위해서는 다음 두 가지를 수행해야합니다.

  • 먼저 다음과 ActionName같은 속성 으로 작업을 장식하십시오.

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
    
  • 둘째, WebApiConfig파일 에서 다음 경로를 정의 하십시오.

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );
    

ID 유형에 대한 제한을 설정하지 않으려면 어떻게합니까? 의미 : 문자열 ID를 어떻게 받아 들일 수 있습니까?
frapontillo

5
@frapontillo : Id는 정수 이름이어야합니다. 따라서 경로 이름과 구별되지 않으면 라우팅 enghine은 ID가 아니라 동작 이름으로 처리합니다. ID를 문자열로 사용해야하는 경우 조치를 작성할 수 있습니다.
Asif Mushtaq

대신 속성 라우팅을 사용합니다. WebApiConfig에서 여러 경로를 사용하지 않아도됩니다. 다음 링크를 확인하십시오. docs.microsoft.com/en-us/aspnet/web-api/overview/…
Rich

이렇게 추가하면 오류가 발생합니다 ------------ namespace ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; 세트; }} [ActionName ( "ProductController")] public class ProductController : ApiController {// GET : api / NewCotroller public IEnumerable <string> Get () {return new string [] { "value1", "value2"}; }
Umashankar

41

문제에 대한 훨씬 더 나은 해결책 Route은 주석으로 메소드의 경로를 지정할 수있는 사용 하는 것입니다 .

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

어떤 네임 스페이스가 경로에 있습니까? MVC4를 사용하고 있고 Route가 인식되지 않습니다.
eaglei22


그렇습니다. 감사.
newman

1
어떤 이유로 나는 이것을 작동시킬 수 없다. 이것은 내가 이미하고있는 일입니다.
oligofren

2
어떻게 URL 모양보다 같이 호출하는 것 RouteTSPRoute?
Si8

27

사용하다:

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

더 이상 RESTful 접근 방식은 아니지만 이제 웹 API가 동사를 기반으로 자동으로 결정하도록하는 대신 이름으로 작업을 호출 할 수 있습니다.

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

일반적인 생각과는 달리이 방법에는 아무런 문제가 없으며 웹 API를 남용하지 않습니다. Web API의 모든 멋진 기능 (델리 게이터, 콘텐츠 협상, 미디어 형식 포맷터 등)을 여전히 활용할 수 있습니다. RESTful 방식을 버릴뿐입니다.


1
답변 주셔서 감사하지만 여전히 같은 오류가 발생합니다.
Habib

가능하지 않다면 앱에서 다른 것이 잘못 구성되어야합니다. 전체 경로 설정을 보여줄 수 있습니까? 또한 컨트롤러 작업을 정확히 어떻게 호출합니까?
Filip W

전체 경로 설정은 global.asax에 있습니다. 내 질문에 해당 부분을 게시했습니다. 요청을 위해 Fiddler-> Compose->를 사용하고 Post를 작업으로 선택하고 있습니다
Habib

다른 모든 경로 정의를 제거하고 내가 게시 한 경로를 그대로 두십시오. 그런 다음 한 컨트롤러 내에있는 POST 작업을 모두 쉽게 호출 할 수 있습니다 (이전 MVC 접근 방식과 동일)
Filip W

1
Filip, 저는 mvc4 또는 Visual studio 2012 RC와 함께 .Net framework 4.5를 사용하고 있습니다. 어떤 템플릿을 사용하여 프로젝트를 만들면 완벽하게 작동합니다
Habib

13

웹 API 엔드 포인트 (컨트롤러)는 get / post / put / delete 동사를 허용하는 단일 리소스입니다. 일반적인 MVC 컨트롤러 가 아닙니다 .

필요한 경우 보내는 매개 변수를 허용하는 하나의 HttpPost 메서드 /api/VTRouting만있을 수 있습니다 . [http] 항목으로 꾸미는 한 함수 이름 은 중요하지 않습니다 . 그래도 시도한 적이 없습니다.

편집 : 작동하지 않습니다. 해결하면 유형에 모델 바인딩을 시도하지 않고 매개 변수의 수를 따르는 것 같습니다.

다른 매개 변수를 허용하도록 함수를 오버로드 할 수 있습니다. 나는 당신이하는 방식으로 선언했지만 메소드에 다른 (호환되지 않는) 매개 변수를 사용하면 괜찮을 것이라고 확신합니다. 매개 변수가 동일하면 모델 바인딩이 어떤 것을 의미하는지 알 수 없으므로 운이 좋지 않습니다.

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

이 부분은 작동합니다

새 템플릿을 만들 때 제공되는 기본 템플릿은 이것을 매우 명백하게 만듭니다.이 규칙을 고수해야한다고 말하고 싶습니다.

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

많은 작업을 수행하는 하나의 클래스를 Ajax 사용을 위해 만들고 싶다면 표준 컨트롤러 / 액션 패턴을 사용하지 않는 큰 이유가 없습니다. 유일한 차이점은 메소드 서명이 예쁘지 않다는 것이므로 Json( returnValue)반환하기 전에 물건을 감싸 야 합니다.

편집하다:

간단한 유형을 사용할 때 표준 템플릿 (포함하도록 편집)을 사용할 때 오버로드가 제대로 작동합니다. 다른 서명을 가진 2 개의 사용자 정의 객체를 사용하여 다른 방법으로 테스트했습니다. 그것을 작동시킬 수 없었습니다.

  • 복잡한 객체와의 바인딩은 "깊게"보이지 않으므로 그렇게 할 필요가 없습니다.
  • 쿼리 문자열에 추가 매개 변수를 전달하여이 문제를 해결할 수 있습니다.
  • 사용 가능한 옵션을 제공 할 수있는 것보다 더 나은 기록

이것은이 경우 나를 위해 일했습니다, 그것이 당신을 얻는 곳을보십시오. 테스트 만 예외입니다.

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

그리고 콘솔에서 다음과 같이 호출됩니다.

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )

매개 변수 유형을 변경하려고 시도했지만 컨트롤러에서 단일 Post 메소드 만 허용하는 것 같습니다. 답장을 보내 주셔서 감사합니다
Habib

오버로드 할 수 있기 때문에 모델 바인딩을 찾아 모델을 찾으려고 가정했습니다. 그러나 다른 #의 매개 변수와 함께 작동합니다. 그렇게하기 위해 다시 작성하는 것이 그리 어렵지는 않지만, 아직 소스 코드를 공개하지 않았기 때문에 추악한 분해를보고 있습니다.
Andrew Backer

2
작동하지 않는 이유와 웹 API의 철학을 실제로 설명하는 +1
MEMark

고장을 인정하십시오 ... 컨트롤러 당 하나의 POST / PUT / GET이어야한다고 가정했지만 확실하지 않았습니다 ... 그래서 내가 찾은 이유. 컨트롤러 당 여러 유사한 동작이 표준 인 웹 응용 프로그램 용 MVC로 개발을 시작한 이래로 거의 낭비로 보이므로 개발자가 원하는 이유를 이해할 수 있습니다. 너무 많은 컨트롤러가 있습니까?
Anthony Griggs

6

동일한 Web API Controller에 여러 Get 및 Post 메소드를 추가 할 수 있습니다. 여기서 기본 경로는 문제를 일으키는 것입니다. 웹 API는 위에서 아래로 일치하는 경로를 확인하므로 모든 요청에 ​​대해 기본 경로가 일치합니다. 기본 경로에 따라 하나의 제어기에서 단 하나의 Get and Post 메소드 만 가능합니다. 다음 코드를 맨 위에 배치하거나 주석 처리 / 삭제 기본 경로

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

1

컨트롤러 수준에서 경로 접두사 [RoutePrefix ( "api / Profiles")]를 넣고 동작 방법 [Route ( "LikeProfile")]에 경로를 붙입니다. global.asax 파일에서 아무것도 변경할 필요가 없습니다.

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}

0

나는 그 질문에 이미 대답했다고 생각합니다. 또한 서명 된 mehtod는 있지만 이름은 다른 webApi 컨트롤러를 찾고있었습니다. WebApi로 계산기를 구현하려고했습니다. 계산기에는 서명은 같지만 이름이 다른 4 가지 방법이 있습니다.

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

WebApiConfig 파일에는 이미

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

IIS에서 인증 / 권한을 설정하기 만하면됩니다!

도움이 되었기를 바랍니다!


0

이 방법을 사용할 수 있습니다 :

public class VTRoutingController : ApiController
{
    [HttpPost("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

오버로드 get / post 메소드가 restfull api의 개념을 위반하는지 확실하지 않지만 작동합니다. 누군가이 문제에 대해 깨달을 수 있다면. 내가 소변을 보이면

uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid

내 저널 종류의 집계 루트를 볼 수 있듯이 게시를 위해 다른 컨트롤러를 정의하고 내 URL에 게시 번호를 전달할 수는 있지만 훨씬 더 의미가 있습니다. 내 출판물이 저널 자체 없이는 존재하지 않았기 때문입니다.


-1

방금 URL에 "action = action_name"을 추가했습니다. 이렇게하면 라우팅 엔진이 원하는 작업을 알 수 있습니다. 또한 ActionName 속성을 작업에 추가했지만 필요한지 확실하지 않습니다.


-1

내가이 주제에서 본 가장 간단하고 간단한 설명-http: //www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • 수정 됨-

Route 만 사용하고 RoutePrefix가 필요하지 않았습니다.

예를 들어 컨트롤러에서

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

그런 다음 함수 이름은 jquery에 다음 중 하나로 나타납니다.

options.url = "/api/customer/PostCustomer";

또는

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