2009-10-14 28 views
5

Estoy usando el patrón Redirect After Post en mi aplicación ASP.NET MVC. Tengo el siguiente escenario:Redirigir después de publicar en ASP.NET MVC

  1. usuario va a /controller/index donde se le pide que rellene un formulario.
  2. Los valores de formulario se envían a /controller/calculate.
  3. La acción Calculate realiza un cálculo basado en la entrada y crea una instancia de un objeto complejo que contiene los resultados de la operación. Este objeto se almacena en TempData y el usuario se redirige al /controller/result.
  4. /controller/result recupera el resultado de TempData y se lo presenta al usuario.

El problema con este enfoque es que si el usuario pulsa F5 mientras observa los resultados en /controller/result la página ya no se puede representar como TempData ha expirado y el objeto de resultado ya no está disponible.

Este comportamiento no es deseado por los usuarios. Una posible solución sería en lugar de redirigir después del POST, simplemente renderizando la vista de resultados. Ahora, si el usuario pulsa F5, obtiene un diálogo del navegador que le pregunta si desea volver a publicar el formulario. Esto tampoco fue deseado.

Una solución posible que pensé fue serializar el objeto resultante y pasarlo en la URL antes de redirigir pero AFAIK hay algunas limitaciones en la longitud de una solicitud GET y si el objeto se pone bastante grande podría llegar a esta limitación (especialmente si está codificado en base64).

Otra posibilidad sería utilizar el objeto Session en lugar de TempData para persistir en los resultados. Pero antes de implementar esta solución, me gustaría saber si hay una mejor manera de hacerlo.


ACTUALIZACIÓN:

Además de investigar el tema descubrí que si vuelvo a poner el objeto de resultado en TempData dentro de la acción /controller/result que realmente funciona:

public ActionResult Result() 
{ 
    var result = TempData["result"]; 
    TempData["result"] = result; 
    return View(result); 
} 

Pero esto se siente especie de sucio. ¿Podría haber algún efecto secundario con este enfoque (como cambiar a proveedores de sesiones fuera del proceso ya que actualmente uso InProc)?

+0

Cuando dice "redirigir" está llamando RedirectToAction? – Will

+0

Sí, RedirectToAction. –

Respuesta

5

Almacénelo en la sesión con alguna clave única y pase la clave como parte de la url. Luego, mientras la sesión esté activa, pueden usar el botón atrás/adelante al contenido de su corazón y aún así tener la URL correcta.Alternativamente, podría usar el caché ASP, pero normalmente lo reservaría para los objetos que se comparten entre los usuarios. Por supuesto, si usó los parámetros para el cálculo como la clave y encontró el resultado en la caché, simplemente podría volver a usarlo.

+0

Gracias, es una gran sugerencia. Intentaría implementarlo con una clave única aprobada en la url. –

1

TempData generalmente se considera útil para pasar mensajes de vuelta al usuario, no para almacenar entidades activas (una actualización del usuario nukeará los contenidos de TempData).

No conozco el lugar más apropiado que la sesión para almacenar este tipo de información. Creo que la idea general es mantener la sesión lo más pequeña posible. Personalmente, suelo escribir algunas envolturas para agregar y eliminar objetos específicos a la sesión. Limpiarlos manualmente cuando sea posible.

Como alternativa, puede almacenar en una base de datos en la que purga elementos obsoletos con regularidad.

2

Creo que redirigir después de la publicación tiene mucho más sentido cuando la URL resultante es significativa. En su caso, significa que todos los datos necesarios para el cálculo se encuentran en la URL de/controlador/resultado.

/controlador/Calcular no lo haría, pero el cálculo/controlador/resultado.

Si puede hacer esto, piensa que es muy fácil: tiene los valores necesarios para el cálculo y los usa como la clave para el caché. Si el usuario se actualiza, solo accede al caché.

Si no puede tener una url significativa, puede publicar en/controller/index. Si el usuario pulsa F5, el cálculo volvería a comenzar, pero una caché con el hash como clave lo ayudaría de nuevo.

+0

Necesito validar la entrada antes de calcular y si hay un error, mostrar la misma forma con los valores precargados y los campos de error marcados en rojo. Si se realizó el cálculo en la acción Result, ¿cómo manejaría los errores de ModelState? ¿Debería volver a dirigirme a la acción Índice y volver a poner todos los valores de entrada y los errores de validación en el TempData para poder mostrar el formulario correctamente? –

+0

Es más difícil tener una validación en el resultado. Me gustaría redirigir a ella con valores validados. La forma más fácil es publicar para indexar y validar los datos/mostrar errores allí. –

0

Podría adoptar una idea similar a la de muchos bancos en sus sitios de banca en línea mediante el uso de claves de un solo uso para verificar todos los POST. Puede integrarlo en un helper html para formularios y en su capa de servicio (por ejemplo) para la verificación.

Digamos que sólo desea publicar ningún ejemplo de una forma de una sola vez. Agregue un guid al formulario. Si el formulario no se publica y los datos se confirman, quiere invalidar el guid y redirigirlo a la acción GET. Si el formulario no es válido, cuando la página se publique, necesitará un nuevo guión (válido) en el formulario esperando el siguiente intento de publicación.

GUID se generan como se requiere y se añade a una tabla en su base de datos. Como están invalidados (por POSTS, sean exitosos o no) están marcados en la tabla. Es posible que desee recortar la tabla en 100 filas ... o en 1000, dependiendo de qué tan pesada será su aplicación y cuántos formularios presentados, pero aún no publicados, puede tener en cualquier momento.

realmente no he afinado este diseño, pero creo que podría funcionar. No será tan mal olor como el TempData y todavía puede adherirse al patrón PRG.

Recuerde, con PRG no desea enviar los datos nuevos a la acción GET en una variable de temperatura de algún tipo. Desea volver a consultarlo desde el almacén de datos, donde ahora está comprometido.

+0

Actualmente la aplicación no tiene un almacén de datos. Realiza todo el trabajo en memoria (solo cálculos y sin persistencia). Es por eso que consideré usar TempData o Session como un almacén de datos temporal. –

0

Como dijo Michael, TempData tiene un único propósito:> almacenar un objeto para un solo viaje y un solo viaje. Según tengo entendido, TempData está utilizando esencialmente el mismo objeto Session que podría usar, pero eliminará automáticamente el objeto de la sesión en el siguiente viaje.

Palo con la Sesión en mi humilde opinión en lugar de empujar de nuevo a TempData.

Cuestiones relacionadas