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/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。
【问题讨论】:
【参考方案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您的问题的另一个解决方案是使用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/… 是的,这是应该的方式。谢谢。 由于某种原因我无法让它工作。这正是我已经在做的事情。 与调用Route
和TSPRoute
相比,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 方法的共享逻辑