MVC 路由未保留在 RedirectToAction 上
MVC route not kept on RedirectToAction
我有这样的路由配置:
routes.MapRoute(
"routeB",
"routeB/{action}/{id}",
new { controller = "Sample", action = "IndexB", id = UrlParameter.Optional });
routes.MapRoute(
"routeA",
"routeA/{action}/{id}",
new { controller = "Sample", action = "IndexA", id = UrlParameter.Optional });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
我的示例控制器包含这些操作方法:
public ActionResult IndexA(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexA()
{
return RedirectToAction("Confirmation");
}
public ActionResult Confirmation()
{
return View("Confirmation");
}
public ActionResult IndexB(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexB()
{
return RedirectToAction("Confirmation");
}
如果我登陆 localhost/RouteA 页面并创建 POST(通过按钮),它会将我重定向到 localhost/RouteB/Confirmation。
如何让页面重定向到 RouteA/Confirmation 页面?
谢谢。
你的问题很简单。
查看您的控制器,IndexA 和 IndexB 方法,returns 相同的视图; (来自您的评论)
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; }
<h2>Index</h2>
using (Html.BeginForm("IndexB", "Sample", new { @id = "OrderForm" })) {
@Html.AntiForgeryToken()
<button type="submit" id="orderFormBtn">Extend my discount.</button>
}
当您点击提交时,您总是会执行 post 到 IndexB
有很多方法可以纠正这种简单的错误,例如,您可以简单地使用 2 个不同的视图 IndexA
和 IndexB
并更改
using (Html.BeginForm("IndexB", "Sample", new { @id = "OrderForm" })) {
到
using (Html.BeginForm("IndexA", "Sample", new { @id = "OrderForm" })) {
问题是您的 routeA
和 routeB
路由都可以为 SampleController
中的 IndexA
操作创建 link。因此,BeginForm
只会 short-circuit 并选择第一条可行的路线,这可能是也可能不是您正在寻找的 "correct" 路线。为了区分路由,通过路由名称生成它们:
@using (Html.BeginRouteForm("routeA", new { @id = "OrderForm" })) {
然而,这只会让你获得 "default" routeA
路线,即 /routeA/
。除了路由的默认操作之外,无法指定其他操作。
为了获得更大的灵活性,您可以使用属性路由,这将允许您为每个操作指定一个自定义路由名称,然后您可以使用它来获取该操作的 URL。
除此之外,您将需要以某种方式区分这两条路线,以便在生成 URL 时应该使用哪条路线不会产生歧义。使用标准路由通常很难做到这一点,这就是为什么属性路由是更好的方法,如果您不打算对所有内容简单地使用默认路由。
或者,您可以重组项目以保持相同的 URL 结构,但更容易区分路线。通过使用区域,您可以指定应从中创建路径的区域。例如,假设您有 RouteA
和 RouteB
区域,您可以这样做:
@using (Html.BeginForm("IndexB", "Sample", new { area = "RouteA", @id = "OrderForm" })) {
那就意味着每个区域都有一个 SampleController
,但是您可以使用继承来重用代码。
这里有两个问题。正如其他人指出的那样,您的 HTTP POST 需要更正。由于您要为 2 个不同的操作共享一个视图,因此最简单的方法是将 actionName
参数设置为 null
。这告诉 MVC 使用当前请求中的操作名称。
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; }
<h2>Index</h2>
@using (Html.BeginForm(null, "Sample", new { id = "OrderForm" }))
{
@Html.AntiForgeryToken()
<button type="submit" id="orderFormBtn">Extend my discount.</button>
}
第二个问题是在生成 URL 时,RedirectToAction
调用在 routeA
和 routeB
之间不明确。由于第一个匹配项总是获胜,因此您重定向到的 URL 始终是您配置中的第一个匹配项。
您可以通过使用 RedirectToRoute
明确指定路由名称(除了您现有的匹配条件之外)来解决此问题。
public class SampleController : Controller
{
public ActionResult IndexA(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexA()
{
return RedirectToRoute("routeA", new { action = "Confirmation" });
}
public ActionResult Confirmation()
{
return View("Confirmation");
}
public ActionResult IndexB(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexB()
{
// Note that changing this one to RedirectToRoute is not strictly
// necessary, but may be more clear to future analysis of the configuration.
return RedirectToRoute("routeB", new { action = "Confirmation" });
}
}
我有这样的路由配置:
routes.MapRoute(
"routeB",
"routeB/{action}/{id}",
new { controller = "Sample", action = "IndexB", id = UrlParameter.Optional });
routes.MapRoute(
"routeA",
"routeA/{action}/{id}",
new { controller = "Sample", action = "IndexA", id = UrlParameter.Optional });
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
我的示例控制器包含这些操作方法:
public ActionResult IndexA(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexA()
{
return RedirectToAction("Confirmation");
}
public ActionResult Confirmation()
{
return View("Confirmation");
}
public ActionResult IndexB(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexB()
{
return RedirectToAction("Confirmation");
}
如果我登陆 localhost/RouteA 页面并创建 POST(通过按钮),它会将我重定向到 localhost/RouteB/Confirmation。
如何让页面重定向到 RouteA/Confirmation 页面?
谢谢。
你的问题很简单。 查看您的控制器,IndexA 和 IndexB 方法,returns 相同的视图; (来自您的评论)
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; }
<h2>Index</h2>
using (Html.BeginForm("IndexB", "Sample", new { @id = "OrderForm" })) {
@Html.AntiForgeryToken()
<button type="submit" id="orderFormBtn">Extend my discount.</button>
}
当您点击提交时,您总是会执行 post 到 IndexB
有很多方法可以纠正这种简单的错误,例如,您可以简单地使用 2 个不同的视图 IndexA
和 IndexB
并更改
using (Html.BeginForm("IndexB", "Sample", new { @id = "OrderForm" })) {
到
using (Html.BeginForm("IndexA", "Sample", new { @id = "OrderForm" })) {
问题是您的 routeA
和 routeB
路由都可以为 SampleController
中的 IndexA
操作创建 link。因此,BeginForm
只会 short-circuit 并选择第一条可行的路线,这可能是也可能不是您正在寻找的 "correct" 路线。为了区分路由,通过路由名称生成它们:
@using (Html.BeginRouteForm("routeA", new { @id = "OrderForm" })) {
然而,这只会让你获得 "default" routeA
路线,即 /routeA/
。除了路由的默认操作之外,无法指定其他操作。
为了获得更大的灵活性,您可以使用属性路由,这将允许您为每个操作指定一个自定义路由名称,然后您可以使用它来获取该操作的 URL。
除此之外,您将需要以某种方式区分这两条路线,以便在生成 URL 时应该使用哪条路线不会产生歧义。使用标准路由通常很难做到这一点,这就是为什么属性路由是更好的方法,如果您不打算对所有内容简单地使用默认路由。
或者,您可以重组项目以保持相同的 URL 结构,但更容易区分路线。通过使用区域,您可以指定应从中创建路径的区域。例如,假设您有 RouteA
和 RouteB
区域,您可以这样做:
@using (Html.BeginForm("IndexB", "Sample", new { area = "RouteA", @id = "OrderForm" })) {
那就意味着每个区域都有一个 SampleController
,但是您可以使用继承来重用代码。
这里有两个问题。正如其他人指出的那样,您的 HTTP POST 需要更正。由于您要为 2 个不同的操作共享一个视图,因此最简单的方法是将 actionName
参数设置为 null
。这告诉 MVC 使用当前请求中的操作名称。
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; }
<h2>Index</h2>
@using (Html.BeginForm(null, "Sample", new { id = "OrderForm" }))
{
@Html.AntiForgeryToken()
<button type="submit" id="orderFormBtn">Extend my discount.</button>
}
第二个问题是在生成 URL 时,RedirectToAction
调用在 routeA
和 routeB
之间不明确。由于第一个匹配项总是获胜,因此您重定向到的 URL 始终是您配置中的第一个匹配项。
您可以通过使用 RedirectToRoute
明确指定路由名称(除了您现有的匹配条件之外)来解决此问题。
public class SampleController : Controller
{
public ActionResult IndexA(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexA()
{
return RedirectToRoute("routeA", new { action = "Confirmation" });
}
public ActionResult Confirmation()
{
return View("Confirmation");
}
public ActionResult IndexB(string id)
{
return View("Index");
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult IndexB()
{
// Note that changing this one to RedirectToRoute is not strictly
// necessary, but may be more clear to future analysis of the configuration.
return RedirectToRoute("routeB", new { action = "Confirmation" });
}
}