web-dev-qa-db-ja.com

ASP.NET MVC Core / 6:複数の送信ボタン

コントローラーで異なるアクションを実行するには、複数の送信ボタンが必要です。

私はここでエレガントなソリューションを見ました: ASP.NET MVCフレームワークで複数の送信ボタンをどのように処理しますか? このソリューションでは、アクションメソッドをカスタム属性で装飾できます。ルートが処理されると、このカスタム属性のメソッドは、属性のプロパティがクリックされた送信ボタンの名前と一致するかどうかをチェックします。

しかし、MVCコア(RC2ナイトリービルド)では、ActionNameSelectorAttributeが見つかりません(Githubリポジトリも検索しました)。 ActionMethodSelectorAttributeを使用する同様のソリューションを見つけました( http://www.dotnetcurry.com/aspnet-mvc/724/handle-multiple-submit-buttons-aspnet-mvc-action-methods )。

ActionMethodSelectorAttributeは使用できますが、メソッドIsValidForRequestの署名は異なります。タイプRouteContextのパラメーターがあります。しかし、投稿データが見つかりませんでした。したがって、カスタム属性プロパティと比較するものは何もありません。

以前のMVCバージョンのソリューションと同様に、MVCコアで利用できる同様のエレガントなソリューションはありますか?

32
noox

私はこれを以前に行いましたが、以前は、フォームをさまざまなコントローラーアクションに投稿していました。問題は、サーバー側の検証エラーで次のいずれかで行き詰まっていることです。

  1. return View(vm)は、投稿アクション名をURLに残します…残念です。
  2. return Redirect(...)は、TempDataを保存するためにModelStateを使用する必要があります。また残念。

これが私が選択したことです。

  1. ボタンのnameを使用して、POSTで変数にバインドします。
  2. ボタンvalueは、送信アクションを区別するための列挙型です。 Enumはタイプセーフであり、switchステートメントでより適切に機能します。 ;)
  3. GETと同じアクション名にPOSTします。これにより、サーバー側の検証エラー時にURLにPOSTアクション名が表示されなくなります。
  4. 検証エラーがある場合は、適切なPGRパターンに従って、ビューモデルとreturn View(viewModel)を再構築します。

この手法を使用すると、TempData!を使用する必要はありません。

私の使用例では、「ロールの追加」および「ロールの削除」アクションを含む「ユーザー/詳細」ページがあります。

ここにボタンがあります。入力タグの代わりにボタンにすることもできます...;)

<button type="submit" class="btn btn-primary" name="SubmitAction" value="@UserDetailsSubmitAction.RemoveRole">Remove Role</button>
<button type="submit" class="btn btn-primary" name="SubmitAction" value="@UserDetailsSubmitAction.AddRole">Add Users to Role</button>

これがコントローラーのアクションです。読みやすくするために、switchコードブロックを独自の関数にリファクタリングしました。 2つの異なるビューモデルに投稿する必要があるため、1つは入力されませんが、モデルバインダーは気にしません!

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Details(
    SelectedUserRoleViewModel removeRoleViewModel, 
    SelectedRoleViewModel addRoleViewModel,
    UserDetailsSubmitAction submitAction)
{
    switch (submitAction)
    {
        case UserDetailsSubmitAction.AddRole:
        {
            return await AddRole(addRoleViewModel);
        }
        case UserDetailsSubmitAction.RemoveRole:
        {
            return await RemoveRole(removeRoleViewModel);
        }
        default:
            throw new ArgumentOutOfRangeException(nameof(submitAction), submitAction, null);
    }
}

private async Task<IActionResult> RemoveRole(SelectedUserRoleViewModel removeRoleViewModel)
{
    if (!ModelState.IsValid)
    {
        var viewModel = await _userService.GetDetailsViewModel(removeRoleViewModel.UserId);
        return View(viewModel);
    }

    await _userRoleService.Remove(removeRoleViewModel.SelectedUserRoleId);

    return Redirect(Request.Headers["Referer"].ToString());
}

private async Task<IActionResult> AddRole(SelectedRoleViewModel addRoleViewModel)
{
    if (!ModelState.IsValid)
    {
        var viewModel = await _userService.GetDetailsViewModel(addRoleViewModel.UserId);
        return View(viewModel);
    }

    await _userRoleService.Add(addRoleViewModel);

    return Redirect(Request.Headers["Referer"].ToString());
}

別の方法として、 AJAXを使用してフォームを投稿 することもできます。

0
Jess