Как я могу поддерживать ModelState с RedirectToAction? - PullRequest
67 голосов
/ 11 ноября 2008

Как я могу вернуть результат другого действия или переместить пользователя на другое действие, если в моем ModelState произошла ошибка без потери информации ModelState?

Сценарий есть; Действие Удалить принимает POST из формы DELETE, представленной моим Индексом Action / View. Если в «Удалить» произошла ошибка, я хочу переместить пользователя обратно в действие / представление «Индекс» и показать ошибки, сохраненные действием «Удалить» в ViewData.ModelState. Как это можно сделать в ASP.NET MVC?

[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Delete)]
public ActionResult Delete([ModelBinder(typeof(RdfUriBinder))] RdfUri graphUri)
{
    if (!ModelState.IsValid)
        return Index(); //this needs to be replaced with something that works :)

    return RedirectToAction("Index");
}

Ответы [ 5 ]

94 голосов
/ 11 ноября 2008

Сохраните ваши данные представления в TempData и извлеките их оттуда в действии Index, если оно существует.

   ...
   if (!ModelState.IsValid)
       TempData["ViewData"] = ViewData;

   RedirectToAction( "Index" );
}

 public ActionResult Index()
 {
     if (TempData["ViewData"] != null)
     {
         ViewData = (ViewDataDictionary)TempData["ViewData"];
     }

     ...
 }

[EDIT] Я проверил источник в Интернете для MVC, и кажется, что ViewData в контроллере можно установить, поэтому, вероятно, проще всего просто перенести все ViewData, включая ModelState, в действие Index. 1004 *

40 голосов
/ 22 апреля 2009

Использовать фильтры действий (шаблон PRG) (так же просто, как использовать атрибуты)

Упоминается здесь и здесь .

10 голосов
/ 11 ноября 2008

Обратите внимание, что решение tvanfosson не всегда будет работать, хотя в большинстве случаев это должно быть просто отлично.

Проблема с этим конкретным решением состоит в том, что если у вас уже есть какие-либо ViewData или ModelState, вы в конечном итоге перезаписываете все это с состоянием предыдущего запроса. Например, новый запрос может содержать некоторые ошибки состояния модели, связанные с неверными параметрами, передаваемыми действию, но в итоге они будут скрыты, поскольку они перезаписаны.

Другая ситуация, когда она может работать не так, как ожидалось, - это наличие фильтра действий, который инициализировал некоторые ошибки ViewData или ModelState. Опять же, они будут перезаписаны этим кодом.

Мы рассматриваем некоторые решения для ASP.NET MVC, которые позволили бы вам легче объединять состояния из двух запросов, поэтому следите за обновлениями.

Спасибо, Eilon

5 голосов
/ 20 августа 2013

В случае, если это кому-нибудь пригодится, я использовал рекомендованное @bob решение с использованием PRG:

см. Пункт 13 -> ссылка .

У меня была дополнительная проблема с сообщениями, передаваемыми в VeiwBag в View, которые записывались и проверялись / загружались вручную из TempData в действиях контроллера при выполнении RedirectToAction("Action"). В попытке упростить (а также сделать его обслуживаемым) я немного расширил этот подход для проверки и хранения / загрузки других данных. Мои методы действия выглядели примерно так:

 [AcceptVerbs(HttpVerbs.Post)]
 [ExportModelStateToTempData]
 public ActionResult ChangePassword(ProfileViewModel pVM) {
      bool result = MyChangePasswordCode(pVM.ChangePasswordViewModel);
      if (result) {
           ViewBag.Message = "Password change success";
      else {
           ModelState.AddModelError("ChangePassword", "Some password error");
      }
      return RedirectToAction("Index");
    }

И мой Индекс Действие:

[ImportModelStateFromTempData]
public ActionResult Index() {
    ProfileViewModel pVM = new ProfileViewModel { //setup }
    return View(pVM);
}

Код в фильтрах действий:

// Following best practices as listed here for storing / restoring model data:
// http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg
public abstract class ModelStateTempDataTransfer : ActionFilterAttribute {
    protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName;
}

public class ExportModelStateToTempData : ModelStateTempDataTransfer {
    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        //Only export when ModelState is not valid
        if (!filterContext.Controller.ViewData.ModelState.IsValid) {
            //Export if we are redirecting
            if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) {
                filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
            }
        }
        // Added to pull message from ViewBag
        if (!string.IsNullOrEmpty(filterContext.Controller.ViewBag.Message)) {
            filterContext.Controller.TempData["Message"] = filterContext.Controller.ViewBag.Message;
        }

        base.OnActionExecuted(filterContext);
    }
}

public class ImportModelStateFromTempData : ModelStateTempDataTransfer {
    public override void OnActionExecuted(ActionExecutedContext filterContext) {
        ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;

        if (modelState != null) {
            //Only Import if we are viewing
            if (filterContext.Result is ViewResult) {
                filterContext.Controller.ViewData.ModelState.Merge(modelState);
            } else {
                //Otherwise remove it.
                filterContext.Controller.TempData.Remove(Key);
            }
        }
        // Restore Viewbag message
        if (!string.IsNullOrEmpty((string)filterContext.Controller.TempData["Message"])) {
            filterContext.Controller.ViewBag.Message = filterContext.Controller.TempData["Message"];
        }

        base.OnActionExecuted(filterContext);
    }
}

Я понимаю, что мои изменения здесь являются довольно очевидным расширением того, что уже было сделано с ModelState с помощью кода @ по ссылке, предоставленной @bob - но мне пришлось наткнуться на этот поток, прежде чем я даже подумал об обработке этого способ.

0 голосов
/ 11 ноября 2008

Может быть попробовать

return View("Index");

вместо

return Index();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...