ChatGPT解决这个技术问题 Extra ChatGPT

你能在 ASP.NET MVC 中重载控制器方法吗?

我很想知道您是否可以在 ASP.NET MVC 中重载控制器方法。每当我尝试时,我都会收到以下错误。这两种方法接受不同的参数。这是不能做的事情吗?

当前对控制器类型“MyController”的操作“MyMethod”的请求在以下操作方法之间不明确:

@andy 对于 mvc 4 也是一样的 :)
和 mvc 5 一样
和 mvc 6 一样
与 MVC Core 1.1 相同
与 MVC Core 2.0 相同

J
JD Conley

如果您希望代码进行重载,可以使用该属性。

[ActionName("MyOverloadedName")]

但是,您必须为同一个 http 方法使用不同的操作名称(正如其他人所说)。所以这只是当时的语义。您宁愿在代码中使用名称还是在属性中使用名称?

Phil 有一篇与此相关的文章:http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx


使用这个和重载你的动作的主要缺点是它不能再由同一个视图文件呈现。
实际上,它仍然可以渲染相同的视图文件。您只需要指定视图的名称,而不是盲目地调用 return View();。例如:return View("MyOverloadedName");
@JD 但微软说.. 用作控制器操作的方法不能重载.. 你可以在这里看到它..asp.net/mvc/tutorials/controllers-and-routing/…
t
tvanfosson

是的。我已经能够通过将每个控制器方法的 HttpGet/HttpPost(或等效的 AcceptVerbs 属性)设置为不同的东西来做到这一点,即 HttpGetHttpPost,但不能同时设置两者。这样它就可以根据请求的类型来判断使用哪种方法。

[HttpGet]
public ActionResult Show()
{
   ...
}

[HttpPost]
public ActionResult Show( string userName )
{
   ...
}

我的一个建议是,对于这样的情况,应该有一个私有实现,你的两个公共 Action 方法都依赖它来避免重复代码。


对于 MVC2 及更高版本,还可以使用 HttpPost/HttpGet 属性
@yohal 是的,如果您不需要支持多个动词,那将是现在处理它的规范方法。
请注意不要滥用它来违反 REST 的原则。
很确定这只是因为您的 Show() 方法具有不同的签名才有效。如果并且当您需要将信息发送到 Get 版本时,您的 Get 和 Post 版本最终会具有相同的签名,并且您需要 ActionName 属性或本文中提到的其他修复之一。
@ScottK.Fraley 这是真的。如果他们需要相同的签名,您必须以不同的方式命名并应用 ActionNameAttribute。在实践中,我很少发现这种情况。
J
Josh

这是您可以做的其他事情......您想要一个能够有参数而不是没有参数的方法。

为什么不试试这个...

public ActionResult Show( string username = null )
{
   ...
}

这对我有用......在这种方法中,您实际上可以测试是否有传入参数。


string 不能为空。)
字符串可以为空。事实上,它已经可以为空,只是不需要'?'
@ProfK - 不,字符串是可以为空的引用类型。它不是“可空的”。 Nullable 意味着您正在使用 Nullable (即 T?)。乔希的观点是你不能把 ?在 string 之后,因为它不是值类型,并且 Nullable 只接受值类型。
我随机找到了回到这个问题的方法,然后意识到我在上面发表了评论。不记得这个了……很奇怪! string 不能是 nullable 仍然是正确的;但它可以是null!无论哪种方式,我都没有诚意地发布了最初的评论。
S
Shivprasad Koirala

不,不,不。去试试下面我们有“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


谢谢你。我想你也可以从一开始就使用不同的动作名称而不是使用属性。
@Dan 但是我们在 C# 端没有多态性。
你是对的,没有控制器方法重载,但它与 HTTP 无关。
感谢您的澄清。 +1。应该考虑更多的 HTTP 而不是 C#。没有理由使用 OO 策略来处理操作。
I
Ian Mercer

为了解决这个问题,您可以编写一个 ActionMethodSelectorAttribute,检查每个操作的 MethodInfo,并将其与发布的表单值进行比较,然后拒绝任何表单值不匹配的方法(当然,不包括按钮名称)。

这是一个示例:- http://blog.abodit.com/2010/02/asp-net-mvc-ambiguous-match/

但是,这不是一个好主意。


@Cerbrus 因为这是一个可怕的黑客攻击,下一个查看您的控制器代码的人会被一种非常非标准的方法所迷惑。
呵呵,还算公道。
k
keeney

据我所知,您只能在使用不同的 http 方法时使用相同的方法。

IE

[AcceptVerbs("GET")]
public ActionResult MyAction()
{

}

[AcceptVerbs("POST")]
public ActionResult MyAction(FormResult fm)
{

}

装饰与过载无关。它是允许重载的参数列表。
@SkySanders 我不同意,基于参数的重载在 MVC 控制器方法中不起作用——你有一个可行的例子吗?干杯。
使用 [HttpPost] 属性而不是 [AcceptVerbs("POST")]
c
cookdn

我在 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)
{
    //.....
}

希望这会有所帮助,并且不会导致某人走错路。 :-)


干得好!我刚遇到这个问题,你救了我!我也有“x”年的 WebForms - 所以仍然是一个学习曲线。现在没有 MVC 就找不到工作哈哈
D
DevDave

您可以使用单个 ActionResult 来处理 PostGet

public ActionResult Example() {
   if (Request.HttpMethod.ToUpperInvariant() == "GET") {
    // GET
   }
   else if (Request.HttpMethod.ToUpperInvariant() == "POST") {
     // Post  
   }
}

如果您的 GetPost 方法具有匹配的签名,则很有用。


嗯,有点像重新发明轮子,但这次是方形的。为什么不简单地使用 [HttpPost/Get] 属性?
已经有一段时间了,但我认为我这样做是因为 MVC 没有区分具有匹配信号的两个单独的方法。我使用的是 HttpPost 属性,尽管我没有将 HttpGet 放在其他方法上。
@DevDave 以及两种方法的归属,请确保您使用的是 system.web.mvc 中的属性 - 而不是 system.web.http 中的属性!
D
DvS

我刚刚遇到这个问题,尽管它现在已经很老了,但它仍然非常相关。具有讽刺意味的是,该线程中的一个正确评论是由一位自认是 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/)。

祝你好运!


A
Alex Butenko

您可以使用 [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");
    }
}

K
Kasey Speakman

我需要一个重载:

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);

        }

A
Andiih

将基本方法创建为虚拟

public virtual ActionResult Index()

创建被覆盖的方法作为覆盖

public override ActionResult Index()

编辑:这显然只适用于重写方法在派生类中,而派生类似乎不是 OP 的意图。


你可能误解了这个问题。 OP 要求在同一个控制器中重载该方法,而不是在派生类中覆盖它。
@Andiih:如果两种方法都在同一个控制器中会发生什么?
C
Community

我喜欢在另一个帖子中发布的这个答案

这主要用于如果您从另一个控制器继承并希望覆盖来自基本控制器的操作

ASP.NET MVC - Overriding an action with differing parameters


M
Matt

每个控制器方法只允许一个公共签名。如果您尝试重载它,它会编译,但您会遇到您遇到的运行时错误。

如果您不愿意使用不同的动词(如 [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 只需要一个。


E
Eric Saboia

抱歉耽搁了。我遇到了同样的问题,我找到了一个很好答案的链接,这对新人有帮助吗

BinaryIntellect 网站和作者的所有学分

基本上有四种情况:使用不同的动词,使用路由,使用[NoAction]属性重载标记和使用[ActionName]更改动作属性名称

所以,这取决于你的要求和你的情况。

但是,请点击链接:

链接:http://www.binaryintellect.net/articles/8f9d9a8f-7abf-4df6-be8a-9895882ab562.aspx


a
alanextar

对于那些在同一问题上苦苦挣扎的人来说,这个答案。您可以基于 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 包不再可用。


P
Panos Roditakis

如果这是尝试对多个视图使用一个 GET 操作,这些视图 POST 到具有不同模型的多个操作,则尝试为每个重定向到第一个 GET 的 POST 操作添加一个 GET 操作,以防止刷新时出现 404。

远射但常见的场景。