Web API 控制器中的多个 HttpPost 方法

Posted

技术标签:

【中文标题】Web API 控制器中的多个 HttpPost 方法【英文标题】:Multiple HttpPost method in Web API controller 【发布时间】:2012-07-09 13:44:30 【问题描述】:

我开始使用 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/TSPRoutehttp://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。

【问题讨论】:

【参考方案1】:

您可以在单个控制器中执行多个操作。

为此,您必须做以下两件事。

首先用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:Id 应该是一个整数,以便它与路由名称区分开来,否则路由引擎会将其视为操作名称而不是 id。如果您需要将 id 作为字符串,那么您可以创建一个操作。 我会改用属性路由。您不必以这种方式在 WebApiConfig 中使用多个路由。查看此链接:docs.microsoft.com/en-us/aspnet/web-api/overview/… 如果我像这样添加它会给我一个错误------------ namespace ImageDownloadApplication.Controllers public class FrontModel public string skus get;放; [ActionName("ProductController")] public class ProductController : ApiController // GET: api/NewCotroller public IEnumerable Get() return new string[] "value1", "value2" ; 【参考方案2】:

您的问题的另一个解决方案是使用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。 查找以下2个来源asp.net/web-api/overview/web-api-routing-and-actions/…asp.net/web-api/overview/web-api-routing-and-actions/… 是的,这是应该的方式。谢谢。 由于某种原因我无法让它工作。这正是我已经在做的事情。 与调用RouteTSPRoute 相比,URL 看起来如何?【参考方案3】:

使用:

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 的所有出色功能(委托处理程序、内容协商、媒体类型格式化程序等)——您只需放弃 RESTful 方法。

【讨论】:

感谢您的回答,但它仍然给我同样的错误。 这是不可能的,那么您的应用程序中必须配置错误。你能显示整个路线设置吗?另外,您如何确切地调用控制器操作? 整个路由设置在 global.asax 中,我已经在我的问题中发布了那部分,为了提出请求,我使用 Fiddler->Compose-> 并选择 Post 作为操作 尝试删除所有其他路由定义并留下我发布的那个。然后,您可以轻松调用位于一个控制器中的两个 POST 操作(与旧的 MVC 方法相同) Filip,我正在使用 .Net framework 4.5,带有 mvc4 或 Visual Studio 2012 RC,您使用哪个模板来创建您的项目,您的工作完美【参考方案4】:

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 个具有不同签名的自定义对象。永远无法让它工作。

与复杂对象的绑定看起来并不“深”,所以这是不行的 您可以通过在查询字符串上传递一个额外的参数来解决此问题 A better writeup than I can give 可用选项

在这种情况下,这对我有用,看看它会带给你什么。仅用于测试的例外情况。

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 方法。感谢您的回复 我假设它会尝试模型绑定来找到它,因为你可以重载。不过,它适用于不同的 # 参数。虽然重写它可能并不难,但他们还没有发布源代码,所以我只是看着丑陋的反汇编 +1 实际解释了它不起作用的原因,以及 web api 背后的哲学。 欣赏故障...我以为每个控制器应该是一个 POST/PUT/GET 但我不确定...这就是我查找它的原因。自从我开始使用 MVC 为 Web 应用程序开发,每个控制器的多个类似操作是常态......这几乎看起来是一种浪费,所以我可以理解为什么开发人员想要这样做。是不是控制器太多了?【参考方案5】:

可以在同一个 Web API 控制器中添加多个 Get 和 Post 方法。这里默认路由是导致问题的原因。 Web API 检查从上到下的匹配路由,因此您的默认路由匹配所有请求。根据默认路由,一个控制器中只能使用一种 Get 和 Post 方法。将以下代码放在顶部或注释掉/删除默认路由

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

【讨论】:

【参考方案6】:

将路由前缀 [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()
        
        
    

【讨论】:

【参考方案7】:

您可以使用这种方法:

public class VTRoutingController : ApiController

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

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

【讨论】:

【参考方案8】:

创建另一个 Http 方法时添加[HttpPost("Description")]

[HttpPost("Method1")]
public DataType Method1(MyRequestTemplate routingRequestTemplate)

    return null;


[HttpPost("Method2")]
public DataType Method2(MyRequestTemplate routingRequestTemplate)

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案9】:

我认为这个问题已经得到解答。我还在寻找具有相同签名方法但名称不同的 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 上设置身份验证/授权即可!

希望这会有所帮助!

【讨论】:

【参考方案10】:

我在这个话题上看到的最好和最简单的解释 - 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";

【讨论】:

这是完美的答案,不知道为什么被否决。【参考方案11】:
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 作为

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

所以你可能会看到我的日志类型的聚合根,尽管我可以单独定义另一个控制器来发布,并在我的 url 中传递发布的 id 编号,但这更有意义。因为如果没有期刊本身,我的出版物就不会存在。

【讨论】:

以上是关于Web API 控制器中的多个 HttpPost 方法的主要内容,如果未能解决你的问题,请参考以下文章

具有多个 HttpPost 方法的 Web Api 中的 CORS 问题

Web API 控制器中多个 HttpPost 方法的共享逻辑

无法使用具有多个参数的 MVC Web Api HttpPost 修饰操作

具有多个参数的 Web API 路由

Web API Post 方法 frombody 为空

从 ASP.NET MVC Web API 中的多个表单数据键接收文件