desvioEn el post anterior de esta serie vimos cómo la forma más sencilla de transferir a un usuario desde una página a otra es mediante el uso de una redirección en el lado cliente, usando Redirect o RedirectPermanent. Sin embargo esto implica dos secuencias de petición-respuesta al servidor y además se visualiza la página final en la barra de direcciones del usuario.

Muchas veces (la mayoría) es probable que sea lo que queremos, pero hay otras situaciones en las que no será así y lo que necesitaremos es que la ejecución se cambie en el servidor, de manera inadvertida para el usuario, por ejemplo:

  1. Ejecutar c√≥digo com√ļn a varias p√°ginas que hace uso de controles y genera elementos para la interfaz de usuario. En la mayor parte de los casos podr√≠amos usar una biblioteca com√ļn o un control de usuario, pero si involucra p√°ginas complejas o, simplemente, si ya tenemos la funcionalidad hecha en un ASPX y no queremos repetir el trabajo, nos vendr√° bien poder transferir la ejecuci√≥n a esta p√°gina.
  2. Reutilizar resultados complejos hechos en una p√°gina para ser procesados en otra. Imaginemos que tenemos una p√°gina que efect√ļa alg√ļn tipo de operaci√≥n costosa como un c√°lculo complejo, acceso a un servicio remoto que tarde bastante en devolver el resultado, obtener un conjunto de datos grande desde una base de datos o desde disco... En funci√≥n del resultado de esta operaci√≥n queremos mostrar una interfaz de usuario u otra (por ejemplo, varios posibles tipos de informe en funci√≥n del resultado), pero queremos aprovechar esos resultados sin complicarnos la vida almacen√°ndolos en alg√ļn lugar intermedio. En este caso una transferencia de ejecuci√≥n desde una p√°gina a otra podr√≠a (eligiendo la apropiada para cada resultado) venirnos muy bien.
  3. Tenemos una página hecha con otra tecnología (como PHP o ASP clásico) y queremos ejecutarla para obtener sus resultados pero no enviar directamente al usuario a la misma.
  4. Existen una serie de páginas protegidas a las que no queremos que el usuario tenga acceso directamente escribiendo su dirección, pero sin embargo sí que queremos usarlas internamente para procesar ciertos datos.

ASP.NET ofrece diversas maneras de cambiar la ejecución de una página a otra dinámicamente, y cada una de ellas tiene sus ventajas, inconvenientes y limitaciones. Con algunas podremos resolver algunos de los casos anteriores y con otras no. Vamos a verlo.

Transferencia directa de la ejecución

ASP.NET hereda des la versión clásica ASP 3.0 un método de servidor llamado Transfer. Este método permite transferir la ejecución actual desde el punto en el que se esté llamando a otra páginas ASPX cualquiera dentro de la misma aplicación.

Así, por ejemplo, si escribo:

   1: Server.Transfer("P2.aspx");

desde un evento de la p√°gina, la ejecuci√≥n de √©sta se detiene de inmediato y se comienza a ejecutar la p√°gina P2.aspx. √Čsta pasar√° por todos los eventos de su ciclo de vida como si de una p√°gina normal se tratara (profundizar√© m√°s sobre esto luego), y se devolver√° su contenido mezclado con el contenido que hubiera hasta ese momento en la p√°gina original. El navegador del usuario seguir√° mostrando la URL de la p√°gina original y para √©l ser√° transparente el hecho de que en el servidor lo que se ha ejecutado es otra p√°gina diferente.

Si te bajas el ejemplo descargable de código, y abres la página Transfer.aspx verás que tiene el siguiente código en su evento Load:

   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   3:     //In this example I'm using Server.Transfer to execute the P2.aspx file.
   4:     //As long as Transfer doesn't create a real new request and the ASP.NET pipeline is not regenerated, 
   5:     //then I'm able to access the contents although the page is protected.
   6:     Response.Write("This text is generated in Transfer.aspx <br>");
   7:     Server.Transfer("P2.aspx");
   8:     Response.Write("This does not appear in the page as long as Transfer breaks the processing of the current page!");
   9: }

Al ejecutarla ver√°s lo siguiente en el navegador:

Transfer

De observar esta figura podemos extraer varias cosas importantes:

  1. La URL original no ha cambiado, y nada hace pensar al usuario que en el servidor se ha ejecutado otra p√°gina en realidad.
  2. En la página final resultante hay mezclado HTML de la primera y de la segunda página, a la que se ha transferido la ejecución.
  3. La √ļltima l√≠nea de c√≥digo de la p√°gina inicial, justo despu√©s del Transfer, no se ha ejecutado.
  4. Estamos viendo el contenido de una p√°gina protegida: a pesar de que la p√°gina P2.aspx est√° protegida con el web.config se ha podido ejecutar sin problemas.

Seguridad

Esto √ļltimo es especialmente importante. El motivo de que se pueda ver el contenido de una p√°gina protegida por HTTP como es P2.aspx, es debido a que la ejecuci√≥n es directa a trav√©s del manejador (handler) actual, apropiado para p√°ginas ASPX, pero en ning√ļn caso se trata de una petici√≥n nueva y por lo tanto no se recorre de nuevo el ciclo de vida de una petici√≥n normal (es decir, no se ejecuta el "pipeline" de una petici√≥n nueva). Debido a ello no se realiza de nuevo la autorizaci√≥n en el servidor ya que ese m√≥dulo HTTP ya se ha ejecutado para la p√°gina original, pero no se ejecuta para esta segunda. Por el mismo motivo cualquier otro tipo de manejador intermedio que hayamos colocado no ser√° procesado tampoco y debemos tenerlo en cuenta. As√≠, aunque la p√°gina P2 en condiciones normales no est√° accesible a trav√©s de una petici√≥n HTTP s√≠ que lo est√° en este caso.

Debo aclarar aquí una cosa que dije antes: aunque no se procesa de nuevo el ciclo de vida de la petición HTTP en el servidor, lo que sí se procesa es todo el ciclo de vida de la propia página, es decir, sus eventos PreInit, Init, Load, etc.... como en el caso de cualquier página normal. Se trata esta de una distinción importante muy a tener en cuenta.

Existe una sobrecarga de este m√©todo Transfer que permite especificar una clase derivada de IHttpHandler de modo que hagamos que sea este tipo de manejador en concreto el que procese la petici√≥n. As√≠ podr√≠amos crear un manejador que efect√ļe ciertas tareas, comprobaciones u operaciones y luego enviar al lado cliente lo que consideremos oportuno. Yo no lo he usado nunca ni s√© de nadie que lo haya hecho, pero ah√≠ est√° por si quieres experimentar ;-)

Colecciones y datos de la p√°gina original.

Por defecto la página además conserva las colecciones Form o QueryString de la petición original. Por ello, desde la página a la que hemos transferido la ejecución podemos obtener cualquier dato de campos enviado a la página original y trabajar con ellos. Existe una sobrecarga del método en la que se pasa como segundo parámetro un booleano para indicar si se deben borrar o no esas colecciones.

De todos modos lo interesante es poder acceder a otro tipo de informaci√≥n albergada en la p√°gina original. Imaginemos el ejemplo 2 de la lista del principio de este texto. Si hemos obtenido una informaci√≥n muy costosa desde alguna fuente y queremos que en la p√°gina a la que transferimos √©sta se encuentre disponible y pueda ser utilizada para generar los resultados ¬Ņc√≥mo podemos hacer?

Bien, este es un truco poco conocido, pero como ambas páginas comparten el mismo manejador de petición, es posible obtener una referencia a la primera página a través del contexto de la petición actual.

En el c√≥digo de ejemplo hay una p√°gina TransferAccessPrevious.aspx que ilustra c√≥mo hacerlo. √Čsta dispone de un miembro p√ļblico llamado _sharedData que contiene la informaci√≥n que queremos compartir con la p√°gina transferida (P3.aspx):

   1: public partial class TransferAccessPrevious : System.Web.UI.Page
   2: {
   3:     //This data will be available in the transferred page
   4:     public string _sharedData = "DATA";
   5:  
   6:     protected void Page_Load(object sender, EventArgs e)
   7:     {
   8:         //In this example I'm using Server.Transfer to execute the P3.aspx file.
   9:         //This file access the _sharedData member using a nice trick :-)
  10:         Server.Transfer("P3.aspx");
  11:     }
  12: }

En esta p√°gina P3.aspx, para obtener acceso a _sahredData o cualqueir otro miembro p√ļblico de la p√°gina previa, hacemos lo siguiente:

   1: protected void Page_Load(object sender, EventArgs e)
   2: {
   3:     TransferAccessPrevious pagTransfer = (TransferAccessPrevious)Context.Handler;
   4:     Response.Write(pagTransfer._sharedData);
   5: }

Como vemos lo √ļnico que hemos hecho es obtener una referencia a la p√°gina original, que es del tipo TransferAccessPrevious, haciendo una conversi√≥n expl√≠cita desde el manejador actual del contexto de la petici√≥n. A partir de ah√≠ es una clase normal y podemos acceder a sus m√©todos y miembros, por lo que en la p√°gina final veremos el contenido de la variable _shareddata, o sea, "DATA":

Transfer2

Es un truco sencillo pero, como digo, poco conocido.

En breve publicaré la tercera parte en la que profundizaré sobre la ejecución de páginas y sus implicaciones.

¬ŅTe ha gustado este post? - Aprende .NET con los cursos on-line tutelados de campusMVP:
·
Preparación del examen 70-515: Desarrollo Web con .NET 4.0 (Tutelado por mi)
· Desarrollo Web con ASP.NET 4.0 Web Forms (Tutelado por mi)
· ASP.NET 4.0 Web Forms desde cero (Tutelado por mi)
· Desarrollo Web con ASP.NET MVC 3
· Silverlight 4.0 - Aplicaciones Ricas para Internet (RIA)
· jQuery paso a paso para programadores ASP.NET
· Visual Studio 2010 desde cero

>> Pedir una cosa y recibir otra

  • Parte I: Redirect y RedirectPermanent
  • Parte II: Transferir la ejecuci√≥n
  • Parte III: Ejecutar otras p√°ginas
  • Parte IV: Llamadas de servidor que recrean la petici√≥n completa

💪🏻 ¬ŅEste post te ha ayudado?, ¬Ņhas aprendido algo nuevo?
Pues NO te pido que me invites a un café... Te pido algo más fácil y mucho mejor

Escrito por un humano, no por una IA