我很想知道您是否可以在 ASP.NET MVC 中重载控制器方法。每当我尝试时,我都会收到以下错误。这两种方法接受不同的参数。这是不能做的事情吗?
当前对控制器类型“MyController”的操作“MyMethod”的请求在以下操作方法之间不明确:
如果您希望代码进行重载,可以使用该属性。
[ActionName("MyOverloadedName")]
但是,您必须为同一个 http 方法使用不同的操作名称(正如其他人所说)。所以这只是当时的语义。您宁愿在代码中使用名称还是在属性中使用名称?
Phil 有一篇与此相关的文章:http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
是的。我已经能够通过将每个控制器方法的 HttpGet
/HttpPost
(或等效的 AcceptVerbs
属性)设置为不同的东西来做到这一点,即 HttpGet
或 HttpPost
,但不能同时设置两者。这样它就可以根据请求的类型来判断使用哪种方法。
[HttpGet]
public ActionResult Show()
{
...
}
[HttpPost]
public ActionResult Show( string userName )
{
...
}
我的一个建议是,对于这样的情况,应该有一个私有实现,你的两个公共 Action 方法都依赖它来避免重复代码。
Show()
方法具有不同的签名才有效。如果并且当您需要将信息发送到 Get 版本时,您的 Get 和 Post 版本最终会具有相同的签名,并且您需要 ActionName
属性或本文中提到的其他修复之一。
ActionNameAttribute
。在实践中,我很少发现这种情况。
这是您可以做的其他事情......您想要一个能够有参数而不是没有参数的方法。
为什么不试试这个...
public ActionResult Show( string username = null )
{
...
}
这对我有用......在这种方法中,您实际上可以测试是否有传入参数。
string
不能为空。)
string
不能是 nullable
仍然是正确的;但它可以是null
!无论哪种方式,我都没有诚意地发布了最初的评论。
不,不,不。去试试下面我们有“LoadCustomer”重载的控制器代码。
public class CustomerController : Controller
{
//
// GET: /Customer/
public ActionResult LoadCustomer()
{
return Content("LoadCustomer");
}
public ActionResult LoadCustomer(string str)
{
return Content("LoadCustomer with a string");
}
}
如果您尝试调用“LoadCustomer”操作,您将收到如下图所示的错误。
https://i.stack.imgur.com/Hvc7n.png
多态性是 C# 编程的一部分,而 HTTP 是一种协议。 HTTP 不理解多态性。 HTTP 适用于概念或 URL,而 URL 只能具有唯一名称。所以HTTP没有实现多态。
为了解决同样的问题,我们需要使用“ActionName”属性。
public class CustomerController : Controller
{
//
// GET: /Customer/
public ActionResult LoadCustomer()
{
return Content("LoadCustomer");
}
[ActionName("LoadCustomerbyName")]
public ActionResult LoadCustomer(string str)
{
return Content("LoadCustomer with a string");
}
}
因此,现在如果您调用 URL“Customer/LoadCustomer”,将调用“LoadCustomer”操作,并使用 URL 结构“Customer/LoadCustomerByName”调用“LoadCustomer(string str)”。
https://i.stack.imgur.com/rVByW.png
https://i.stack.imgur.com/1Tu2N.png
我从这篇codeproject文章中得到的上述答案--> MVC Action overloading
为了解决这个问题,您可以编写一个 ActionMethodSelectorAttribute
,检查每个操作的 MethodInfo
,并将其与发布的表单值进行比较,然后拒绝任何表单值不匹配的方法(当然,不包括按钮名称)。
这是一个示例:- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/
但是,这不是一个好主意。
据我所知,您只能在使用不同的 http 方法时使用相同的方法。
IE
[AcceptVerbs("GET")]
public ActionResult MyAction()
{
}
[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{
}
[HttpPost]
属性而不是 [AcceptVerbs("POST")]
。
我在 MVC5 中的 Attribute Routing 的帮助下实现了这一点。诚然,我是使用 WebForms 进行 Web 开发十年的新手,但以下内容对我有用。与公认的答案不同,这允许所有重载的动作由同一个视图文件呈现。
首先在 App_Start/RouteConfig.cs 中启用属性路由。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
可以选择使用默认路由前缀装饰您的控制器类。
[RoutePrefix("Returns")]
public class ReturnsController : BaseController
{
//.......
然后用一个共同的路线和参数来装饰你的控制器动作,这些动作相互过载。使用类型约束参数,您可以使用具有不同类型 ID 的相同 URI 格式。
[HttpGet]
// Returns
public ActionResult Index()
{
//.....
}
[HttpGet]
[Route("View")]
// Returns/View
public ActionResult View()
{
// I wouldn't really do this but it proves the concept.
int id = 7026;
return View(id);
}
[HttpGet]
[Route("View/{id:int}")]
// Returns/View/7003
public ActionResult View(int id)
{
//.....
}
[HttpGet]
[Route("View/{id:Guid}")]
// Returns/View/99300046-0ba4-47db-81bf-ba6e3ac3cf01
public ActionResult View(Guid id)
{
//.....
}
希望这会有所帮助,并且不会导致某人走错路。 :-)
您可以使用单个 ActionResult
来处理 Post
和 Get
:
public ActionResult Example() {
if (Request.HttpMethod.ToUpperInvariant() == "GET") {
// GET
}
else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
// Post
}
}
如果您的 Get
和 Post
方法具有匹配的签名,则很有用。
我刚刚遇到这个问题,尽管它现在已经很老了,但它仍然非常相关。具有讽刺意味的是,该线程中的一个正确评论是由一位自认是 MVC 初学者在撰写帖子时发布的。甚至 ASP.NET 文档也不完全正确。我有一个大项目,我成功地重载了动作方法。
如果人们理解路由,除了简单的 {controller}/{action}/{id} 默认路由模式之外,很明显控制器操作可以使用任何独特的模式进行映射。这里有人谈及多态,说:“HTTP 不懂多态”,但路由与 HTTP 无关。简单地说,它是一种字符串模式匹配的机制。
完成这项工作的最佳方法是使用路由属性,例如:
[RoutePrefix("cars/{country:length(3)}")]
public class CarHireController
{
[Route("{location}/{page:int=1}", Name = "CarHireLocation")]
public ActionResult Index(string country, string location, int page)
{
return Index(country, location, null, page);
}
[Route("{location}/{subLocation}/{page:int=1}", Name = "CarHireSubLocation")]
public ActionResult Index(string country, string location, string subLocation, int page)
{
//The main work goes here
}
}
这些操作将处理像 /cars/usa/new-york
和 /cars/usa/texas/dallas
这样的 URL,它们将分别映射到第一个和第二个 Index 操作。
检查这个示例控制器很明显,它超出了上面提到的默认路由模式。如果您的 url 结构与您的代码命名约定完全匹配,则默认设置效果很好,但情况并非总是如此。代码应该是对域的描述,但 url 通常需要更进一步,因为它们的内容应该基于其他标准,例如 SEO 要求。
默认路由模式的好处是它会自动创建唯一的路由。这是由编译器强制执行的,因为 url 将匹配唯一的控制器类型和成员。滚动您自己的路线模式需要仔细考虑以确保唯一性并且它们有效。
重要提示 一个缺点是使用路由为重载操作生成 url 在基于操作名称时不起作用,例如,当使用 UrlHelper.Action 时。但如果使用命名路由,例如 UrlHelper.RouteUrl,它确实有效。根据备受推崇的消息来源,使用命名路由是无论如何都要走的路(http://haacked.com/archive/2010/11/21/named-routes-to-the-rescue.aspx/)。
祝你好运!
您可以使用 [ActionName("NewActionName")] 来使用具有不同名称的相同方法:
public class HomeController : Controller
{
public ActionResult GetEmpName()
{
return Content("This is the test Message");
}
[ActionName("GetEmpWithCode")]
public ActionResult GetEmpName(string EmpCode)
{
return Content("This is the test Messagewith Overloaded");
}
}
我需要一个重载:
public ActionResult Index(string i);
public ActionResult Index(int groupId, int itemId);
我最终这样做的论据很少:
public ActionResult Index(string i, int? groupId, int? itemId)
{
if (!string.IsNullOrWhitespace(i))
{
// parse i for the id
}
else if (groupId.HasValue && itemId.HasValue)
{
// use groupId and itemId for the id
}
}
这不是一个完美的解决方案,特别是如果你有很多争论,但它对我来说效果很好。
我在我的申请中也遇到了同样的问题。在没有修改任何方法信息的情况下,我在动作头上提供了 [ActionName("SomeMeaningfulName")]。问题解决了
[ActionName("_EmployeeDetailsByModel")]
public PartialViewResult _EmployeeDetails(Employee model)
{
// Some Operation
return PartialView(model);
}
}
[ActionName("_EmployeeDetailsByModelWithPagination")]
public PartialViewResult _EmployeeDetails(Employee model,int Page,int PageSize)
{
// Some Operation
return PartialView(model);
}
将基本方法创建为虚拟
public virtual ActionResult Index()
创建被覆盖的方法作为覆盖
public override ActionResult Index()
编辑:这显然只适用于重写方法在派生类中,而派生类似乎不是 OP 的意图。
我喜欢在另一个帖子中发布的这个答案
这主要用于如果您从另一个控制器继承并希望覆盖来自基本控制器的操作
ASP.NET MVC - Overriding an action with differing parameters
每个控制器方法只允许一个公共签名。如果您尝试重载它,它会编译,但您会遇到您遇到的运行时错误。
如果您不愿意使用不同的动词(如 [HttpGet]
和 [HttpPost]
属性)来区分重载方法(这将起作用)或更改路由,那么剩下的就是您可以提供另一种方法不同的名称,或者您可以在现有方法内部调度。我是这样做的:
我曾经遇到过必须保持向后兼容性的情况。原始方法需要两个参数,但新方法只有一个。以我预期的方式重载不起作用,因为 MVC 不再找到入口点。
为了解决这个问题,我做了以下事情:
将 2 个重载的操作方法从公共更改为私有创建了一个新的公共方法,其中包含“仅”2 个字符串参数。那个充当调度员,即: public ActionResult DoSomething(string param1, string param2) { if (string.IsNullOrEmpty(param2)) { return DoSomething(ProductName: param1); } else { int oldId = int.Parse(param1);返回DoSomething(OldParam:param1,OldId:oldId); } } private ActionResult DoSomething(string OldParam, int OldId) { // 这里的一些代码 return Json(result); } private ActionResult DoSomething(string ProductName) { // 这里的一些代码 return Json(result); }
当然,这是一个 hack,应该稍后重构。但就目前而言,它对我有用。
您还可以创建一个调度程序,如:
public ActionResult DoSomething(string action, string param1, string param2)
{
switch (action)
{
case "update":
return UpdateAction(param1, param2);
case "remove":
return DeleteAction(param1);
}
}
可以看到,UpdateAction 需要 2 个参数,而 DeleteAction 只需要一个。
抱歉耽搁了。我遇到了同样的问题,我找到了一个很好答案的链接,这对新人有帮助吗
BinaryIntellect 网站和作者的所有学分
基本上有四种情况:使用不同的动词,使用路由,使用[NoAction]属性重载标记和使用[ActionName]更改动作属性名称
所以,这取决于你的要求和你的情况。
但是,请点击链接:
链接:http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx
对于那些在同一问题上苦苦挣扎的人来说,这个答案。您可以基于 ActionMethodSelectorAttribute 实现自己的自定义过滤器。在这里,我找到了解决您问题的最佳解决方案。在 .net 5 项目上运行良好。如果您尝试实现与 Web api 控制器中相同的逻辑,请使用 Microsoft.AspNetCore.Mvc.WebApiCompatShim。此 nuget 包提供 ASP.NET Core MVC 与 ASP.NET Web API 2 的兼容性,以简化现有 Web API 实现的迁移。请检查此答案,但请考虑从 ASP.NET Core 3.0 开始,Microsoft.AspNetCore.Mvc.WebApiCompatShim 包不再可用。
如果这是尝试对多个视图使用一个 GET 操作,这些视图 POST 到具有不同模型的多个操作,则尝试为每个重定向到第一个 GET 的 POST 操作添加一个 GET 操作,以防止刷新时出现 404。
远射但常见的场景。
return View();
。例如:return View("MyOverloadedName");
。