[asp.net-mvc] ASP.NET MVC-RedirectToAction에서 ModelState 오류를 보존하는 방법?
다음 두 가지 작업 방법이 있습니다 (질문을 위해 단순화 됨).
[HttpGet]
public ActionResult Create(string uniqueUri)
{
// get some stuff based on uniqueuri, set in ViewData.
return View();
}
[HttpPost]
public ActionResult Create(Review review)
{
// validate review
if (validatedOk)
{
return RedirectToAction("Details", new { postId = review.PostId});
}
else
{
ModelState.AddModelError("ReviewErrors", "some error occured");
return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
}
}
따라서 유효성 검사가 통과되면 다른 페이지로 리디렉션됩니다 (확인).
오류가 발생하면 오류가있는 페이지를 표시해야합니다.
내가 return View()
하면 오류가 표시되지만 return RedirectToAction
위와 같이하면 모델 오류가 손실됩니다.
나는 문제에 놀라지 않고 너희들이 이것을 어떻게 처리하는지 궁금해?
물론 리디렉션 대신 동일한 뷰를 반환 할 수 있지만 뷰 데이터를 채우는 “Create”메서드에는 복제해야하는 논리가 있습니다.
어떤 제안?
답변
작업 Review
에 동일한 인스턴스가 있어야합니다 HttpGet
. 그렇게하려면 작업시 Review review
임시 변수에 개체 를 저장 한 HttpPost
다음 작업시 복원해야 HttpGet
합니다.
[HttpGet]
public ActionResult Create(string uniqueUri)
{
//Restore
Review review = TempData["Review"] as Review;
// get some stuff based on uniqueuri, set in ViewData.
return View(review);
}
[HttpPost]
public ActionResult Create(Review review)
{
//Save your object
TempData["Review"] = review;
// validate review
if (validatedOk)
{
return RedirectToAction("Details", new { postId = review.PostId});
}
else
{
ModelState.AddModelError("ReviewErrors", "some error occured");
return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
}
}
작업을 처음 실행 한 후 브라우저를 새로 고쳐도이 HttpGet
작업을 수행하려면 다음을 수행 할 수 있습니다.
Review review = TempData["Review"] as Review;
TempData["Review"] = review;
그렇지 않으면에 review
데이터가 없기 때문에 새로 고침 버튼 개체 가 비어 있습니다 TempData["Review"]
.
답변
나는 오늘이 문제를 직접 해결해야했고이 질문을 발견했습니다.
일부 답변은 유용하지만 (TempData 사용) 실제로 당면한 질문에 답변하지는 않습니다.
내가 찾은 최고의 조언은이 블로그 게시물에 있습니다.
http://www.jefclaes.be/2012/06/persisting-model-state-when-using-prg.html
기본적으로 TempData를 사용하여 ModelState 개체를 저장하고 복원합니다. 그러나 이것을 속성으로 추상화하면 훨씬 깨끗합니다.
예
public class SetTempDataModelStateAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.Controller.TempData["ModelState"] =
filterContext.Controller.ViewData.ModelState;
}
}
public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (filterContext.Controller.TempData.ContainsKey("ModelState"))
{
filterContext.Controller.ViewData.ModelState.Merge(
(ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);
}
}
}
그런 다음 예제에 따라 다음과 같이 ModelState를 저장 / 복원 할 수 있습니다.
[HttpGet]
[RestoreModelStateFromTempData]
public ActionResult Create(string uniqueUri)
{
// get some stuff based on uniqueuri, set in ViewData.
return View();
}
[HttpPost]
[SetTempDataModelState]
public ActionResult Create(Review review)
{
// validate review
if (validatedOk)
{
return RedirectToAction("Details", new { postId = review.PostId});
}
else
{
ModelState.AddModelError("ReviewErrors", "some error occured");
return RedirectToAction("Create", new { uniqueUri = Request.RequestContext.RouteData.Values["uniqueUri"]});
}
}
(bigb가 제안한대로) TempData에서 모델을 전달하려는 경우에도 그렇게 할 수 있습니다.
답변
“Create”메서드의 논리를 사용하여 개인 함수를 만들고 Get 및 Post 메서드에서이 메서드를 호출하고 View ()를 반환하는 것이 어떻습니까?
답변
나는 사용할 수있다 TempData["Errors"]
TempData는 데이터를 한 번 보존하는 모든 작업에 전달됩니다.
답변
뷰를 반환하고 작업의 속성을 통해 중복을 피하는 것이 좋습니다. 다음은 데이터를보기 위해 채우는 예입니다. 메서드 생성 로직으로 비슷한 작업을 수행 할 수 있습니다.
public class GetStuffBasedOnUniqueUriAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var filter = new GetStuffBasedOnUniqueUriFilter();
filter.OnActionExecuting(filterContext);
}
}
public class GetStuffBasedOnUniqueUriFilter : IActionFilter
{
#region IActionFilter Members
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["somekey"] = filterContext.RouteData.Values["uniqueUri"];
}
#endregion
}
예를 들면 다음과 같습니다.
[HttpGet, GetStuffBasedOnUniqueUri]
public ActionResult Create()
{
return View();
}
[HttpPost, GetStuffBasedOnUniqueUri]
public ActionResult Create(Review review)
{
// validate review
if (validatedOk)
{
return RedirectToAction("Details", new { postId = review.PostId });
}
ModelState.AddModelError("ReviewErrors", "some error occured");
return View(review);
}
답변
임시 데이터에 모델 상태를 추가하는 방법이 있습니다. 그런 다음 임시 데이터에서 오류를 확인하는 기본 컨트롤러에 메서드가 있습니다. 그것들이 있으면 ModelState에 다시 추가합니다.
답변
내 시나리오는 PRG 패턴을 사용하고 있으므로 내 ViewModel ( “SummaryVM”)이 TempData에 있고 요약 화면에 표시되므로 조금 더 복잡합니다. 이 페이지에는 일부 정보를 다른 작업에 게시하는 작은 양식이 있습니다. 이 문제는 사용자가이 페이지의 SummaryVM에서 일부 필드를 편집해야한다는 요구 사항에서 비롯되었습니다.
Summary.cshtml에는 생성 할 ModelState 오류를 포착하는 유효성 검사 요약이 있습니다.
@Html.ValidationSummary()
내 양식은 이제 Summary ()에 대한 HttpPost 작업에 POST해야합니다. 편집 된 필드를 나타내는 또 다른 매우 작은 ViewModel이 있으며, modelbinding이이를 가져옵니다.
새로운 형태 :
@using (Html.BeginForm("Summary", "MyController", FormMethod.Post))
{
@Html.Hidden("TelNo") @* // Javascript to update this *@
그리고 행동 …
[HttpPost]
public ActionResult Summary(EditedItemsVM vm)
여기에서 유효성 검사를 수행하고 잘못된 입력을 감지 했으므로 오류가있는 요약 페이지로 돌아 가야합니다. 이를 위해 나는 리디렉션에서 살아남을 TempData를 사용합니다. 데이터에 문제가 없으면 SummaryVM 개체를 복사본으로 바꾼 다음 (물론 편집 된 필드가 변경됨) RedirectToAction ( “NextAction”);
// Telephone number wasn't in the right format
List<string> listOfErrors = new List<string>();
listOfErrors.Add("Telephone Number was not in the correct format. Value supplied was: " + vm.TelNo);
TempData["SummaryEditedErrors"] = listOfErrors;
return RedirectToAction("Summary");
이 모든 것이 시작되는 요약 컨트롤러 작업은 임시 데이터에서 오류를 찾아서 모델 상태에 추가합니다.
[HttpGet]
[OutputCache(Duration = 0)]
public ActionResult Summary()
{
// setup, including retrieval of the viewmodel from TempData...
// And finally if we are coming back to this after a failed attempt to edit some of the fields on the page,
// load the errors stored from TempData.
List<string> editErrors = new List<string>();
object errData = TempData["SummaryEditedErrors"];
if (errData != null)
{
editErrors = (List<string>)errData;
foreach(string err in editErrors)
{
// ValidationSummary() will see these
ModelState.AddModelError("", err);
}
}