ASP.NET MVC 3中的HttpPost动作方法未找到。

4
我有一个简单的表单,它以HttpPost操作方法作为目标,提交后返回其对应的视图。我的问题是,我得到了404 Not Found错误。奇怪的是,如果我将表单方法和操作方法上的属性更改为GET,那么它就可以正常工作并显示TestMethod视图。
看起来我在使用POST时漏掉了什么,但我的其他控制器中的帖子都很正常(例如账户登录和注册)。请注意,AllowAnonymous属性是一个自定义属性,用于指定允许匿名访问的控制器或操作,而不是通过Authorize属性指定需要授权的控制器或操作。我猜没有什么是不可能的,但我认为这与我的问题无关。你认为出了什么问题吗?
表格:
@using (Html.BeginForm("TestMethod", "Test", FormMethod.Post, new { @id = "testForm" })) {
    <fieldset>
        <legend>Test Form</legend>
        <input type="submit" value="Submit" />
    </fieldset>
}

控制器操作:

[AllowAnonymous]
[HttpPost]
public ActionResult TestMethod() {
    return View();
}

查看:

<h2>TestMethod</h2>
<p>HttpPost method was successful.</p>

从 Global.asax.cs 注册路由方法:

public static void RegisterRoutes(RouteCollection routes) {
        routes.IgnoreRoute("favicon.ico");
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        // About
        routes.MapRoute(
            "About", // Route name
            "about", // URL with parameters
            new { controller = "Home", action = "About" } // Parameter defaults
        );

        // Faq
        routes.MapRoute(
            "Faq", // Route name
            "faq", // URL with parameters
            new { controller = "Home", action = "Faq" } // Parameter defaults
        );

        // Glossary
        routes.MapRoute(
            "Glossary", // Route name
            "glossary", // URL with parameters
            new { controller = "Home", action = "Glossary" } // Parameter defaults
        );

        // Register
        routes.MapRoute(
            "Register", // Route name
            "register", // URL with parameters
            new { controller = "Account", action = "Register" } // Parameter defaults
        );

        // LogIn
        routes.MapRoute(
            "LogIn", // Route name
            "login/{id}", // URL with parameters
            new { controller = "Account", action = "LogOn", id = UrlParameter.Optional } // Parameter defaults
        );
        routes.MapRoute(
            "LogOn", // Route name
            "logon/{id}", // URL with parameters
            new { controller = "Account", action = "LogOn", id = UrlParameter.Optional } // Parameter defaults
        );

        // Default
        routes.MapRoute(
            "Default", // Route name
            "{controller}/{action}/{id}", // URL with parameters
            new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
        );

}

授权属性代码:

// AllowAnonymousAttribute class
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class AllowAnonymousAttribute : Attribute { }

// GlobalAuthorize class
public sealed class GlobalAuthorize : AuthorizeAttribute {
    public override void OnAuthorization(AuthorizationContext filterContext) {
        bool skipAuthorization = 
            filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true) || 
            filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true);

        if (!skipAuthorization) base.OnAuthorization(filterContext);
    }
}

// RedirectAuthorizeAttribute class
public class RedirectAuthorizeAttribute : AuthorizeAttribute {
    public string RedirectUrl { get; set; }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
        filterContext.Result = new RedirectResult(RedirectUrl);
    }
}

全局过滤器:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
    filters.Add(new RequireHttpsAttribute());
    filters.Add(new GlobalAuthorize());
    filters.Add(new HandleErrorAttribute());
}

路由重写规则:

<rewrite>
  <rules>
    <!-- Block all requests made to a website that do not have the host header set. -->
    <rule name="Fail bad requests" stopProcessing="true">
      <match url=".*" />
      <conditions>
        <add input="{HTTP_HOST}" pattern="localhost" negate="true" />
      </conditions>
      <action type="AbortRequest" />
    </rule>
    <!-- Remove trailing slash from all incoming requests. -->
    <rule name="Remove trailing slash" stopProcessing="false">
      <match url="(.*)/$" />
      <conditions>
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
      </conditions>
      <action type="Redirect" redirectType="Permanent" url="{R:1}" />
    </rule>
    <!-- Convert all requests to all lowercase. -->
    <rule name="Convert to lowercase" stopProcessing="false">
      <match url=".*[A-Z].*" ignoreCase="false" />
      <action type="Redirect" url="{ToLower:{R:0}}" redirectType="Permanent" />
    </rule>
    <!-- Any URL with (HTTPS == OFF) and (HTTP_HOST with colon) -> use for development testing. -->
    <rule name="Development redirect to HTTPS" enabled="true" stopProcessing="true">
      <match url=".*" ignoreCase="true" />
      <conditions logicalGrouping="MatchAll" trackAllCaptures="true">
        <add input="{HTTPS}" pattern="^OFF$" />
        <add input="{HTTP_HOST}" pattern="([^/:]*?):[^/]*?" />
      </conditions>
      <action type="Redirect" url="https://{C:1}:44300{URL}" />
    </rule>
    <!-- Redirect any HTTP request to HTTPS. -->
    <rule name="Redirect to HTTPS" stopProcessing="true">
      <match url="(.*)" />
      <conditions>
        <add input="{HTTPS}" pattern="^OFF$" ignoreCase="true" />
      </conditions>
      <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
    </rule>
  </rules>
</rewrite>

3
请展示相关的路由表条目。另外,您尝试过移除AllowAnonymous属性吗? - twoflower
@twoflower,我已添加唯一相关的路由表条目。如果我删除AllowAnonymous属性,我将被重定向到登录页面(这是期望的行为)。 - neizan
@nemesv,添加了 AllowAnonymous 属性的代码,以及全局过滤器。 - neizan
@DarinDimitrov,您所说的“路由配置”是什么意思?您是指整个RegisterRoutes方法还是路由重写规则?如果有帮助的话,我已经在Web.config中发布了路由重写规则。如果我误解了,请澄清一下,我会再发更多信息。谢谢。 - neizan
在@DarinDimitrov发表评论之前,我已经忘记了我的应用程序中的重写规则。在过去的三个小时里,我一直在更深入地研究这个问题,我确实认为问题出在重写规则上。虽然我无法确定问题所在,但似乎POST请求在某个地方被更改为GET请求。有人遇到过类似的问题吗?有人知道如何解决吗?如果我保留表单上的FormMethod.Post并只更新控制器操作为HttpGet,它可以工作,我认为这是由于POST被更改为GET。救命啊!谢谢! - neizan
显示剩余4条评论
2个回答

5
我想我终于找到了罪魁祸首。首先,我必须承认,现在知道问题的原因后,这个问题的标题并不是很准确。这个问题大部分与Web.config中的规则重写有关。在回答其他人的评论之前,我完全忘记了规则重写,这就是为什么我一开始没有进一步检查它们的原因。
无论如何,问题出在一个将URL重写为小写的规则上。我知道我的帐户注册和登录表单工作正常,所以我检查了它们,并注意到它们的Html.BeginForm语句是无参的,显然会生成小写的URL。我尝试使用无参POST请求进行测试方法,它可以正常工作。然后,我尝试在Html.BeginForm语句中使用动作和控制器的参数,但这次我将它们输入为小写字符串:Html.BeginForm("testmethod", "test"...)。果然,它也可以正常工作,页面源代码显示表单操作为小写。
要解决我的问题,我只需要设置一个条件来不匹配POST请求:<add input="{REQUEST_METHOD}" matchType="Pattern" pattern="POST" negate="true" />。请注意,问题不是特定于小写规则,而是POST请求被重定向的问题。我找到了一个博客,讨论了POST重定向被转换为GET并导致错误的问题,这正是我遇到的问题。它已经有几年了,但显然仍然是相关信息。
无论如何,现在我已经恢复了。感谢所有提供帮助的人。
附言:当我关闭浏览器标签并结束我的搜索时,我想链接到这个SO问题,因为它与我的问题有关。

0

我刚刚测试了你的代码。一开始我因为没有使用HTTPS而被重定向,所以我禁用了这个属性,但之后你的代码就可以工作了。

以下是我的逻辑推断...

  • 如果HTTPS是问题,你不会得到404错误。
  • 如果你没有登录,你会被重定向到登录页面。

我唯一能想到的是你的控制器要么没有命名为“TestController”,要么你的控制器在一个区域中,你忘记给这个区域提供BeginForm了。这两种情况有没有发生呢?


控制器的名称肯定是“TestController”。关于区域部分,我不太确定。虽然我看到了许多讨论类似问题的区域的问题,但我没有使用过区域。由于我没有有意使用区域,所以我认为那不是它。我是否可能在没有明确编写代码的情况下使用区域?也许我需要更多地了解区域的内容,但是如果我将事物更改为“GET”请求,那么它就可以工作。只有“POST”会给我带来问题。 - neizan
@JPtenBerge,我快速查看了一下区域。我的项目中没有使用区域,所以我不认为是这个问题。无论如何,感谢您的建议。 - neizan
可能不是。您可以通过查看项目结构并查看根目录下是否直接存在“Areas”文件夹来进行检查。如果没有,那么您很可能没有使用区域。此时,路由重写规则将成为我的主要怀疑对象。 - J.P.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接