ASP.NET MVC Core/6:多个提交按钮

noo*_*oox 28 c# asp.net-core-mvc asp.net-core

我需要多个提交按钮来在控制器中执行不同的操作.

我在这里看到了一个优雅的解决方案:如何在ASP.NET MVC Framework中处理多个提交按钮? 使用此解决方案,可以使用自定义属性修饰操作方法.处理路径时,此自定义属性的方法检查属性的属性是否与单击的提交按钮的名称匹配.

但是在MVC Core(RC2 nightly build)中我还没有找到ActionNameSelectorAttribute(我还搜索了Github存储库).我发现了一个类似的解决方案ActionMethodSelectorAttribute(http://www.dotnetcurry.com/aspnet-mvc/724/handle-multiple-submit-buttons-aspnet-mvc-action-methods).

ActionMethodSelectorAttribute可用,但该方法IsValidForRequest具有不同的签名.有一个类型的参数RouteContext.但我找不到那里的帖子数据.所以我没有什么可以与我的自定义属性属性进行比较.

MVC Core中是否有与之前MVC版本类似的优雅解决方案?

Wil*_*Ray 52

您可以使用HTML5 formaction属性,而不是在服务器端路由它.

<form action="" method="post">
    <input type="submit" value="Option 1" formaction="DoWorkOne" />
    <input type="submit" value="Option 2" formaction="DoWorkTwo"/>
</form>
Run Code Online (Sandbox Code Playgroud)

然后只需要像这样的控制器动作:

[HttpPost]
public IActionResult DoWorkOne(TheModel model) { ... }

[HttpPost]
public IActionResult DoWorkTwo(TheModel model) { ... }
Run Code Online (Sandbox Code Playgroud)

可以在此处找到适用于旧版浏览器的优质填充.

请记住......

  1. 当用户按下回车符时,将始终选择第一个提交按钮.
  2. 如果ModelState在发布的操作上发生错误(或其他),则需要将用户发送回正确的视图.(但是,如果您通过AJAX发布,这不是问题.)

  • 啊,好抓!在这种情况下,您需要使用`Url.Action(actionName,controllerName)`帮助器方法为`formaction`属性生成正确的目标. (2认同)
  • @MohammadAkbari这是一个具有多个提交按钮所固有的问题,不幸的是,还发生了之前的MVC4和5的答案.有几种不同的方法可以实现你想要的,但这取决于用例.例如,AJAX表单提交将是一个选项. (2认同)

Sha*_*tin 32

ASP.NET Core 1.1.0具有FormActionTagHelper创建formaction属性的功能.

<form>
    <button asp-action="Login" asp-controller="Account">log in</button>
    <button asp-action="Register" asp-controller="Account">sign up</button>
</form>
Run Code Online (Sandbox Code Playgroud)

这样呈现:

<button formaction="/Account/Login">log in</button>
<button formaction="/Account/Register">sign up</button>
Run Code Online (Sandbox Code Playgroud)

它也适用于或的input标签.type="image"type="submit"

  • 嘿 shuan,我注意到的一件事是,如果我验证模型并且它不正确,则 url 会更改为操作名称。知道如何解决这个问题吗?类似于:{ ModelState.AddModelError(string.Empty, "我们在表单中的数据有问题"); } return View("Edit", editModel); (2认同)

Jes*_*ess 6

我之前已经这样做过,并且在过去我会将表单发布到不同的控制器操作。问题是,在服务器端验证错误中,您要么陷入困境:

\n\n
    \n
  1. return View(vm)将后操作名称留在 url\xe2\x80\xa6 中。
  2. \n
  3. return Redirect(...)需要使用TempData来保存ModelState. 还恶心。
  4. \n
\n\n

这就是我选择做的事情。

\n\n
    \n
  1. 使用name按钮的 绑定到 POST 上的变量。
  2. \n
  3. 该按钮value是一个枚举,用于区分提交操作。Enum 是类型安全的,并且在 switch 语句中效果更好。;)
  4. \n
  5. POST 到与 GET 相同的操作名称。这样,您就不会在服务器端验证错误时在 URL 中获取 POST 操作名称。
  6. \n
  7. 如果存在验证错误,return View(viewModel)请按照正确的 PGR 模式重建视图模型和 。
  8. \n
\n\n

使用此技术,无需使用TempData

\n\n

在我的用例中,我有一个用户/详细信息页面,其中包含“添加角色”和“删除角色”操作。

\n\n

这是按钮。它们可以是按钮而不是输入标签...;)

\n\n
<button type="submit" class="btn btn-primary" name="SubmitAction" value="@UserDetailsSubmitAction.RemoveRole">Remove Role</button>\n<button type="submit" class="btn btn-primary" name="SubmitAction" value="@UserDetailsSubmitAction.AddRole">Add Users to Role</button>\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是控制器操作。我将 switch 代码块重构为它们自己的函数,以使它们更易于阅读。我必须发布到 2 个不同的视图模型,因此不会填充其中一个,但模型绑定器并不关心!

\n\n
[HttpPost]\n[ValidateAntiForgeryToken]\npublic async Task<IActionResult> Details(\n    SelectedUserRoleViewModel removeRoleViewModel, \n    SelectedRoleViewModel addRoleViewModel,\n    UserDetailsSubmitAction submitAction)\n{\n    switch (submitAction)\n    {\n        case UserDetailsSubmitAction.AddRole:\n        {\n            return await AddRole(addRoleViewModel);\n        }\n        case UserDetailsSubmitAction.RemoveRole:\n        {\n            return await RemoveRole(removeRoleViewModel);\n        }\n        default:\n            throw new ArgumentOutOfRangeException(nameof(submitAction), submitAction, null);\n    }\n}\n\nprivate async Task<IActionResult> RemoveRole(SelectedUserRoleViewModel removeRoleViewModel)\n{\n    if (!ModelState.IsValid)\n    {\n        var viewModel = await _userService.GetDetailsViewModel(removeRoleViewModel.UserId);\n        return View(viewModel);\n    }\n\n    await _userRoleService.Remove(removeRoleViewModel.SelectedUserRoleId);\n\n    return Redirect(Request.Headers["Referer"].ToString());\n}\n\nprivate async Task<IActionResult> AddRole(SelectedRoleViewModel addRoleViewModel)\n{\n    if (!ModelState.IsValid)\n    {\n        var viewModel = await _userService.GetDetailsViewModel(addRoleViewModel.UserId);\n        return View(viewModel);\n    }\n\n    await _userRoleService.Add(addRoleViewModel);\n\n    return Redirect(Request.Headers["Referer"].ToString());\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

作为替代方案,您可以使用 AJAX 发布表单

\n