Tengo los siguientes dos métodos de acción (simplificados para la pregunta):
[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"]});
}
}
Entonces, si la validación pasa, redirijo a otra página (confirmación).
Si ocurre un error, necesito mostrar la misma página con el error.
Si lo hago return View()
, se muestra el error, pero si lo hago return RedirectToAction
(como arriba), pierde los errores del modelo.
No me sorprende el problema, solo me pregunto cómo manejan esto.
Por supuesto, podría devolver la misma Vista en lugar de la redirección, pero tengo lógica en el método "Crear" que completa los datos de la vista, que tendría que duplicar.
¿Alguna sugerencia?
Create
método que completa ViewData y llámelo en elCreate
método GET y también en la rama de validación fallida en elCreate
método POST.Create
vista, simplemente lo puse en algún métodopopulateStuff
que llamo tanto en elGET
como en el failPOST
.Respuestas:
Necesita tener la misma instancia de
Review
en suHttpGet
acción. Para hacer eso, debe guardar un objetoReview review
en la variable temporal en suHttpPost
acción y luego restaurarlo en laHttpGet
acción.Si desea que esto funcione incluso si el navegador se actualiza después de la primera ejecución de la
HttpGet
acción, puede hacer esto:De lo contrario, el objeto del botón de actualización
review
estará vacío porque no habrá datosTempData["Review"]
.fuente
TempData["ModelState"] = ModelState;
y restaureModelState.Merge((ModelStateDictionary)TempData["ModelState"]);
, entonces funcionaríareturn Create(uniqueUri)
cuando falla la validación en el POST? Dado que los valores de ModelState tienen prioridad sobre el ViewModel pasado a la vista, los datos publicados deben permanecer.Tuve que resolver este problema hoy yo mismo y encontré esta pregunta.
Algunas de las respuestas son útiles (usando TempData), pero realmente no responden a la pregunta en cuestión.
El mejor consejo que encontré fue en esta publicación de blog:
http://www.jefclaes.be/2012/06/persisting-model-state-when-using-prg.html
Básicamente, use TempData para guardar y restaurar el objeto ModelState. Sin embargo, es mucho más limpio si abstrae esto en atributos.
P.ej
Luego, según su ejemplo, puede guardar / restaurar ModelState así:
Si también desea pasar el modelo en TempData (como sugirió bigb), también puede hacerlo.
fuente
¿Por qué no crear una función privada con la lógica en el método "Create" y llamar a este método desde los métodos Get y Post y simplemente devolver View ().
fuente
return Create(new { uniqueUri = ... });
su lógica permanece SECA (como llamarRedirectToAction
), pero sin los problemas que conlleva la redirección, como perder su ModelState.Podría usar
TempData["Errors"]
Los TempData se pasan a través de acciones conservando los datos 1 vez.
fuente
Le sugiero que devuelva la vista y evite la duplicación mediante un atributo en la acción. A continuación, se muestra un ejemplo de cómo completar para ver datos. Podría hacer algo similar con la lógica de su método de creación.
Aquí hay un ejemplo:
fuente
Tengo un método que agrega el estado del modelo a los datos temporales. Luego tengo un método en mi controlador base que verifica los datos temporales en busca de errores. Si los tiene, los vuelve a agregar a ModelState.
fuente
Mi escenario es un poco más complicado ya que estoy usando el patrón PRG por lo que mi ViewModel ("SummaryVM") está en TempData, y mi pantalla Resumen lo muestra. Hay un pequeño formulario en esta página para PUBLICAR información a otra Acción. La complicación proviene de un requisito para que el usuario edite algunos campos en SummaryVM en esta página.
Summary.cshtml tiene el resumen de validación que detectará los errores de ModelState que crearemos.
Mi formulario ahora necesita PUBLICARSE en una acción HttpPost para Summary (). Tengo otro ViewModel muy pequeño para representar campos editados, y modelbinding me los proporcionará.
La nueva forma:
y la acción ...
Aquí hago una validación y detecto una entrada incorrecta, por lo que necesito volver a la página Resumen con los errores. Para esto utilizo TempData, que sobrevivirá a una redirección. Si no hay ningún problema con los datos, reemplazo el objeto SummaryVM con una copia (pero con los campos editados cambiados, por supuesto) y luego hago un RedirectToAction ("NextAction");
La acción del controlador de resumen, donde todo esto comienza, busca cualquier error en el tempdata y lo agrega al modelstate.
fuente
Microsoft eliminó la capacidad de almacenar tipos de datos complejos en TempData, por lo tanto, las respuestas anteriores ya no funcionan; solo puede almacenar tipos simples como cadenas. He modificado la respuesta de @ asgeo1 para que funcione como se esperaba.
Desde aquí, simplemente puede agregar la anotación de datos requerida en un método de controlador según sea necesario.
fuente
Prefiero agregar un método a mi ViewModel que complete los valores predeterminados:
Luego lo llamo cuando necesito los datos originales como este:
fuente
Estoy dando solo un código de muestra aquí En su viewModel, puede agregar una propiedad de tipo "ModelStateDictionary" como
y en su menthod de acción POST puede escribir código directamente como
y luego asigne este modelo a Tempdata como se muestra a continuación
y cuando redirige al método de acción de otro controlador, en el controlador debe leer el valor de Tempdata
Eso es. No tiene que escribir filtros de acción para esto. Esto es tan simple como el código anterior si desea obtener los errores de estado del modelo en otra vista de otro controlador.
fuente