Web 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中发出请求,在RequestBody中为MyRequestTemplate传递json。

Answers:


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 2013年

5
@frapontillo:ID应该是一个整数,以便与路由名称区别开来,否则路由引擎将把它视为操作名称而不是ID。如果需要将id作为字符串,则可以创建一个动作。
Asif Mushtaq

我会改用属性路由。这样,您就不必在WebApiConfig中使用多个路由。看看这个链接: docs.microsoft.com/en-us/aspnet/web-api/overview/...

如果我这样添加,它将给我一个错误------------名称空间ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; 组; }} [ActionName(“ ProductController”)]公共类ProductController:ApiController {// GET:api / NewCotroller public IEnumerable <string> Get(){返回新字符串[] {“ value1”,“ value2”}; }
Umashankar '18

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,并且无法识别路由。
eaglei22


是的,这就是应该走的路。谢谢。
纽曼

1
由于某种原因,我无法使它正常工作。这正是我已经在做的。
oligofren

2
与调用Route和相比,URL看起来如何TSPRoute
Si8

27

用:

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

它不再是RESTful方法,但是您现在可以按名称调用操作(而不是让Web API根据动词为您自动确定操作),如下所示:

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

与流行的看法相反,这种方法没有什么问题,并且它没有滥用Web API。您仍然可以利用Web API的所有出色功能(代理处理程序,内容协商,mediatypeformatter等),而您无需使用RESTful方法。


1
感谢您的回答,但它仍然给我同样的错误。
哈比卜(Habib)2012年

这是不可能的,那么必须在您的应用程序中配置其他错误。您可以显示整个路线设置吗?另外,您究竟如何称呼控制器动作?
Filip W

整个Route设置位于global.asax中,我已在问题中张贴了该部分,为了提出请求,我使用Fiddler-> Compose->并选择Post作为操作
Habib

尝试删除所有其他路线的定义,只保留我发布的路线。然后,您可以轻松调用位于一个控制器内的两个POST操作(与旧的MVC方法相同)
Filip W

1
Filip,我使用的是.net framework 4.5,mvc4或Visual Studio 2012 RC,您正在使用哪个模板来创建您的项目,您的模板运行得很好
Habib

13

Web API端点(控制器)是一个接受get / post / put / delete动词的资源。它不是普通的MVC控制器。

必然地,/api/VTRouting只有一个 HttpPost方法可以接受您要发送的参数。只要使用[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)2012年

我假设它将尝试模型绑定来找到它,因为您可以重载。但是,它可以与不同的#参数一起使用。重写它可能并不难,但是他们还没有发布源代码,所以我只是呆呆地看着丑陋的反汇编
Andrew Backer 2012年

2
+1,用于实际解释其无法正常工作的原因以及网络API的基本原理。
MEMark 2014年

赞赏故障...我以为每个控制器应该是单个POST / PUT / GET,但是我不确定...因此是我查找它的原因。自从我开始使用针对Web应用程序的MVC进行开发以来,每个控制器都需要执行多个类似的操作,这似乎是一种浪费,所以我可以理解为什么开发人员想要这么做。控制器太多了吗?
Anthony Griggs

6

可以在同一Web API控制器中添加多个Get和Post方法。此处的默认路由为引起问题。Web API会检查从上到下的匹配路由,因此您的默认路由是否与所有请求匹配。根据默认路由,一个控制器中只能使用一种“获取和发布方法”。将以下代码放在顶部或注释掉/删除默认路由

    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

我认为这个问题已经得到解答。我还在寻找具有相同签名方法但名称不同的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

因此,如您所见,我的日记有点像aggregateroot,尽管我可以单独定义另一个发布控制器,并在我的url中传递发布的ID号,但这更有意义。因为没有期刊本身就不会存在我的出版物。


-1

我刚刚在网址中添加了“ 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";
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.