Archivo de Febrero 2009


Usar variables de sesión en HttpHandlers

Jueves, 26 Feb, 2009 @ 13:26 | Por Gustavo Cantero (The Wolf) | ASP.NET

Ayer, luego de escribir el artículo “Usar variables de sesión en servicios web de WCF”, recordé otro lugar donde, por defecto, no se pueden utilizar variables de sesión, pero que muchas veces son necesarias: los HttpHandlers.  Estas clases representan un “handler” que puede accederse a través del navegador pero no necesariamente devuelven una página en HTML o XHTML.  Un ejemplo clásico de una implementación de estas clases es para generar imágenes dinámicamente o buscarla en la base de datos y enviársela al browser, para realizar algún proceso y reenviar al usuario a otra URL o para generar XML para algún componente del lado del cliente, por ejemplo, de Silverlight o Flash.
El esqueleto de la clase es bastante sencillo, es algo como esto:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Download : IHttpHandler {
    public void ProcessRequest(HttpContext Context) {
       ...
    }
    public bool IsReusable {
        get { return false; }
    }
}

Si dentro del método ProcessRequest queremos acceder al objeto System.Web.HttpContext.Current.Session nos va a devolver “null”.  Para que este objeto no sea nulo y nos devuelva la colección de variables de sesión simplemente hay que hacer que nuestra clase, además de heredar de la interfaz IHttpHandler, herede de la interfaz IRequiresSessionState, quedando el esqueleto algo como esto:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Download : IHttpHandler, IRequiresSessionState {
    public void ProcessRequest(HttpContext Context) {
       ...
    }
    public bool IsReusable {
        get { return false; }
    }
}

Con esta pequeña modificación en la herencia de la clase ya podremos acceder al objeto Session sin necesidad de implementar ningún método ni propiedad nueva, ya que el motor de ASP.NET utiliza esta interfaz como una marca para saber si tiene que cargar o no las variables de sesión.

VN:F [1.7.3_972]
Rating: 7.3/10 (3 votos cast)

Usar variables de sesión en servicios web de WCF

Miércoles, 25 Feb, 2009 @ 20:47 | Por Gustavo Cantero (The Wolf) | ASP.NET, WCF

Muchas veces necesitamos guardar información sensible por cada sesión de usuario de una aplicación web, y muchas de esas veces decidimos guardarlas en variables de sesión, ya que son seguras y rápidas, aunque consumen recursos del servidor, pero con un uso medido son una buena solución.  El problema surge cuando estas variables deben ser utilizadas por servicios web de WCF (Windows Communication Foundation), y que al intentar acceder a ellas a través del objeto System.Web.HttpContext.Current.Session nos devuelve la clásica excepción Object reference not set to an instance of an object.  Esto es debido a que la propiedad Current del objeto HttpContext es nula.  La causa de este comportamiento es debido a que en el WCF se desacopló los servicios y sus operaciones de ASP.NET, ya que el transporte de éstos no tiene que ser necesariamente http.

Para poder acceder al contexto web desde un servicio de WCF hay que utilizar el atributo AspNetCompatibilityRequirements para especificar el modo de compatibilidad que va a tener con ASP.NET.  Por ejemplo, si tenemos un servicio llamado “ServiceTest”, y queremos que tenga acceso a las variables de sesión, el mismo debería quedar como esto:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService : ITestService {
  //operaciones del servicio
}

Pero esto no es todo, todavía hay que escribir un poco más.  Hasta ahora marcamos a nuestro servicio como que “puede” utilizar la compatibilidad con ASP.NET, pero si en este punto consultamos la propiedad Current del HttpContext veremos que sigue siendo nula, y peor aún si en lugar de poner “Allowed” en el atributo de compatibilidad pusimos “Required”: se nos va a generar una excepción.  Esto es causado porque aún falta configurar la aplicación para utilizar la compatibilidad con ASP.NET.  Para que los servicios web puedan utilizar esta característica hay que modificar el web.config agregando (o modificando) la siguiente línea dentro de <configuration><system.serviceModel>:

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

Una vez hecho esto ya deberíamos poder acceder a nuestras variables de sesión con un código parecido al siguiente:

object sessionVar = System.Web.HttpContext.Current.Session["MiVariable"];
VN:F [1.7.3_972]
Rating: 8.0/10 (1 voto cast)

Ventanas modales en Silverlight

Martes, 03 Feb, 2009 @ 21:54 | Por Gustavo Cantero (The Wolf) | Silverlight

En Silverlight, como es sabido, no se pueden crear ventanas modales, por lo cual vamos a mostrar como simular esta funcionalidad utilizando un Popup.

El Popup es un objeto del framework de Silverlight 2 que permite mostrar el contenido de un elemento sobre el resto. Con esto podríamos simular la ventana que queremos crear, pero tenemos el problema que alrededor de esta ventana se puede cliquear e interactuar con los elementos que están “por debajo” de ésta. Para evitar esto lo que haremos es crear un elemento “Border” que ocupe toda la página de Silverlight, el cual debe tener un fondo para evitar que el usuario pulse sobre los elementos que están por debajo. Para el fondo podemos utilizar, por ejemplo, el objeto SolidColorBrush con el color negro y una opacidad de 20, para que se pueda ver a través él (si no queremos que se grisee esta zona se puede utilizar el color Colors.Transparent).

El siguiente inconveniente que si el control de Silverlight ocupa un porcentaje de la ventana del navegador (o sea, no tiene un tamaño fijo) y el usuario modifica el tamaño del browser, la sección griseada de la ventana debería modificarse para seguir ocupando el cien por ciento de la página. Para solucionar esto se puede manejar el evento Resized del objeto Application.Current.Host.Content, estableciendo ahí el tamaño del borde del Popup al tamaño actual del objeto Application.Current.RootVisual.

Por último, también tenemos el problema de que, si al Popup le agregamos elementos como un ListBox que tiene interacción con el usuario y necesitamos manejar sus eventos, el Silverlight nos devolverá un error. Para evitar esto simplemente tenemos que agregar el Popup al elemento raíz de nuestra página y, al cerrarlo, quitarlo.

A continuación muestro el código de un método estático

/// <summary>
/// Muestra un objeto en una ventana modal simulada a través de un <see cref="Popup"/>
/// </summary>
/// <param name="Content">Contenido a mostrar en la ventana</param>
public static Popup Show(UIElement Content)
{
    //Botón "cerrar"
    Button btnClose = new Button();
    btnClose.Content = new TextBlock() { Text = "Cerrar" };
    btnClose.HorizontalAlignment = HorizontalAlignment.Center;
    btnClose.Margin = new Thickness(0, 10, 0, 0);

    //Grilla para ubicar el contenido y el botón
    Grid objGridContent = new Grid();
    objGridContent.RowDefinitions.Add(new RowDefinition());
    objGridContent.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
    objGridContent.Children.Add(Content);
    objGridContent.Children.Add(btnClose);
    Grid.SetRow(btnClose, 1);

    //Borde de la ventana
    Border objPopupBorder = new Border();
    objPopupBorder.CornerRadius = new CornerRadius(10);
    objPopupBorder.Background = new SolidColorBrush(Colors.White);
    objPopupBorder.BorderThickness = new Thickness(2);
    objPopupBorder.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 128, 128, 128));
    objPopupBorder.Child = objGridContent;
    objPopupBorder.Padding = new Thickness(10);
    objPopupBorder.Margin = new Thickness(20);
    objPopupBorder.HorizontalAlignment = HorizontalAlignment.Center;
    objPopupBorder.VerticalAlignment = VerticalAlignment.Center;

    //Fondo de la ventana
    Border objBackBorder = new Border();
    objBackBorder.Width = Application.Current.RootVisual.RenderSize.Width;
    objBackBorder.Height = Application.Current.RootVisual.RenderSize.Height;
    objBackBorder.Background = new SolidColorBrush(Color.FromArgb(70, 0, 0, 0));
    objBackBorder.Opacity = 1;
    objBackBorder.Child = objPopupBorder;

    //Popup
    Popup objPopup = new Popup();
    objPopup.Child = objBackBorder;
    ((Application.Current.RootVisual as UserControl).FindName("LayoutRoot") as Grid).Children.Add(objPopup);
    objPopup.Closed += delegate(object sender, EventArgs e)
    {
        //Saco el popup de la grilla
        UIElementCollection objRootElements = ((Application.Current.RootVisual as UserControl).FindName("LayoutRoot") as Grid).Children;
        if (objRootElements.Contains(objPopup))
            objRootElements.Remove(objPopup);
    };
    objPopup.IsOpen = true;

    //Cerramos el popup al pulsar en el botón
    btnClose.Click += delegate(object sender2, RoutedEventArgs e2) { objPopup.IsOpen = false; };

    //Cambio el tamaño del fondo cuando cambia de tamaño el control
    Application.Current.Host.Content.Resized += delegate(object sender, EventArgs e)
    {
        objBackBorder.Width = Application.Current.RootVisual.RenderSize.Width;
        objBackBorder.Height = Application.Current.RootVisual.RenderSize.Height;
    };

    return objPopup;
}

Como siempre, les dejo un proyecto con este código funcionando para que lo prueben. Proyecto de ejemplo de Popup Modal en Silverlight

VN:F [1.7.3_972]
Rating: 8.4/10 (7 votos cast)