MVC 核心表单标签助手继承当前视图 URL 的 action 属性

MVC Core form tag helper that inherit the current view URL for its action attribute

使用 MVC5(和较旧的 MVC 框架),我可以在不指定 controller/action 方法的情况下编写表单:

@using (Html.BeginForm())
{
}

这样,我就可以重用具有不同 URL 的表单。如果从路由“/books/2/edit”调用表单,生成的 HTML 将是:

<form action="/books/2/edit"></form>

如果我使用 URL“/books/add”调用表单,生成的 HTML 将是:

<form action="/books/add"></form>

我怎样才能对标签助手语法做同样的事情?我尝试了各种语法,但它总是生成一个空的操作属性:

<form></form>
<form asp-route=""></form>
<form asp-controller="" asp-action=""></form>

结果:

<form></form>
<form action></form>
<form action></form>

使用 HTML 助手时,未明确提供的值默认为 当前请求 中的路由值。这就是为什么可以不带参数指定 BeginForm 的原因。

使用标签助手时,此默认逻辑不再适用 - 必须明确提供值。没有默认值。

选项 1 - form 标签

模仿 HTML 助手对 form 标签所做的事情的最简单方法是:

<form action="@Url.RouteUrl(this.ViewContext.RouteData.Values)" method="post">
</form>

选项 2 - Html.BeginForm

请注意,您当前的语法在 ASP.NET Core MVC 中仍然有效:

@using (Html.BeginForm())
{
}

但是既然你不得不问这个问题,我会说 完全不清楚 URL 是如何生成的 在使用这种语法时,这意味着你可能应该改用 Url.RouteUrl 来提高可读性,尽管要写的要多一些。

选项 3 - 标签助手

这是一个如何使用标签助手来实现这一点的示例,尽管它有点难看。

有一个表单标签助手属性 asp-all-route-values 允许您在单个参数中传递所有路由值。但是,根据 asp-all-route-data must be IDictionary<string,object> or RouteValueDictionary,无法将 RouteValueDictionary 传递给此属性,您需要将其转换为 IDictionary<string, string>。一种方法是构建一个扩展方法来进行转换。

public static class RouteValueDictionaryExtensions
{
    public static IDictionary<string, string> ToTagHelperValues(this RouteValueDictionary routeValueDictionary)
    {
        var result = new Dictionary<string, string>();
        foreach (var kvp in routeValueDictionary)
        {
            result.Add(kvp.Key, kvp.Value as string);
        }
        return result;
    }
}

然后您可以使用标签助手生成当前 URL,如下所示:

<form asp-all-route-data="@this.ViewContext.RouteData.Values.ToTagHelperValues()">
</form>

选项 4 - 无操作属性

也可以使用没有 action 属性的表单标签。如果省略 action 属性,大多数(如果不是全部)浏览器的默认行为是使用当前的 URL.

<form method="post">
</form>

WARNING: It is not standards compliant to use this option and technically the behavior of browsers do not have to default to the expected behavior of using the current URL if it is not supplied.

到底用哪种方式,看个人喜好了。