HttpModule di ASP.NET per esempi

di Stefano Mostarda, in ASP.NET 2.0,

Le applicazioni web hanno molto spesso la stessa natura e necessitano quindi di operazioni che sono le stesse da sito a sito. Per esempio, ci sono le fasi di autenticazione e di autorizzazione, la possibilità di selezionare una lingua o una skin, o la necessità di gestire le eccezioni in maniera dinamica e flessibile. Queste fasi possono essere inglobate in un unico punto e riutilizzate ovunque grazie agli HttpModule che rappresentano una soluzione elegante ai problemi appena indicati.

Il modello di esecuzione a Pipeline di ASP.NET permette di infiltrarsi in un punto qualunque dell'elaborazione di una richiesta ed intervenire per fare in modo che queste operazioni vengano gestite da moduli esterni che sono per l'appunto gli HttpModule.

Il primo HttpModule

Creare un HttpModule è una cosa abbastanza banale; infatti si deve semplicemente derivare dall'interfaccia IHttpModule ed implementare i due metodi Init e Dispose. Il primo viene invocato durante la fase di istanziazione dell'applicazione ed è qui che si stabilisce in quale fase della pipeline si debba inserire l'HttpModule. Il secondo viene richiamato nella fase di shutdown dell'applicazione ed è qui che si devono rilasciare le risorse eventualmente acquisite precedentemente.

Come detto in precedenza, nel metodo Init si decide in quale fase intervenire; se, ad esempio, si volesse intervenire prima di processare la pagina si dovrebbe intercettare l'evento PreRequestHandlerExecute, mentre per intervenire subito dopo la fine del processo della pagina si dovrebbe utilizzare l'evento PostRequestHandlerExecute. Per fare un esempio di questa tecnica, basta sfruttare i due eventi appena nominati per scrivere un'informazione all'inizio ed alla fine dell'html generato da una pagina.

public class FirstModule : IHttpModule
{
  public void Dispose()
  {
  }

  public void Init(HttpApplication context)
  {
    context.PreRequestHandlerExecute +=
      new EventHandler(context_PreRequestHandlerExecute);
    context.PostRequestHandlerExecute +=
      new EventHandler(context_PostRequestHandlerExecute);
  }

  void context_PostRequestHandlerExecute(object sender, EventArgs e)
  {
    System.Web.HttpContext.Current.Response.Write("fine");
  }

  void context_PreRequestHandlerExecute(object sender, EventArgs e)
  {
    System.Web.HttpContext.Current.Response.Write("inizio");
  }
}

Una volta generato il modulo, questo va configurato all'interno dell'applicazione aggiungendo un elemento add al nodo httpModules nel web.config.

<httpModules>
  <add name="first" type="HttpModuleByExample.FirstModule.FirstModule, HttpModuleByExample"/>
</httpModules>

L'attributo name rappresenta il nome di riferimento dell'HttpModule e deve essere univoco nella lista, mentre l'attributo type specifica la classe del modulo.

Una volta aggiunto il modulo, il risultato è che la scritta "inizio" appare all'inizio della pagina e la scritta "fine" alla fine della stessa.

Gestione degli errori

Qualunque applicazione può andare in errore. Per quanto bene possa essere scritta, esistono casi in cui non è possibile evitare gli errori: database offline, problema hardware, attacchi di malintenzionati, ecc. Per questo motivo è bene avere cura di gestire sempre gli errori e di mostrare all'utente finale solo le informazioni minime (ad esempio, una maschera di errore generica ed un numero per contattare un servizio di help desk), sostituendo la pagina di errore di default di ASP.NET, che riporta informazioni pericolose perchè utilizzabili da malintenzionati per portare attacchi al sistema.

Per venire incontro a questa esigenza, esiste il nodo customErrors nel file web.config il quale permette di configurare una pagina personalizzata che viene inviata a tutti gli utenti (On), lasciare la pagina di default di ASP.NET (Off) o avere una soluzione mista (RemoteOnly) che prevede l'invio della pagina di default per le richieste provenienti dalla macchina locale e la pagina personalizzata per quelle provenienti dall'esterno.

Questa soluzione presenta una serie di inconvenienti non trascurabili. Quando un utente amministratore riceve una errore, potrebbe voler vedere la pagina di default di ASP.NET e non quella "soft". Non solo, se l'applicazione non prevede ruoli, viene meno il principio precedente e quindi può capitare che si debba inviare una pagina in base all'indirizzo IP della richiesta poichè la distinzione locale-remoto potrebbe non essere sufficiente.

Per risolvere il problema in maniera elegante, basta fare ricorso ad un HttpModule che intercetti l'eccezione e analizzi una configurazione personalizzata per decidere l'azione da intraprendere. Innanzitutto si deve creare una sezione di configurazione dove impostare la pagina di errore completa e quali ruoli e indirizzi IP siano autorizzati a vederla.

public class HttpErrorConfigurationElement : ConfigurationSection
{
  [ConfigurationProperty("ip", DefaultValue="", IsRequired=false)]
  public string IP
  {
    get { return (string)this["ip"]; }
    set { this["ip"] = value; }
  }

  [ConfigurationProperty("roles", DefaultValue="", IsRequired=false)]
  public string Roles
  {
    get { return (string)this["roles"]; }
    set { this["roles"] = value; }
  }

  [ConfigurationProperty("fullErrorPage", DefaultValue="",
  IsRequired=false)]
  public string FullErrorPage
  {
    get { return (string)this["fullErrorPage"]; }
    set { this["fullErrorPage"] = value; }
  }
}

Questa classe diventa un nodo nel web.config ed ogni sua proprietà viene mappata con il relativo attributo nel nodo in base al primo valore dell'attributo ConfigurationProperty che decora ogni proprietà. Ad esempio, la proprietà IP viene mappata nell'attributo ip presente nel nodo relativo a questa sezione.

<HttpError roles="Admin" ip="127.0.0.1" fullErrorPage="fullerror.aspx" />

Dopo la parte relativa alla configurazione, si passa alla stesura del codice necessario a soddisfare i parametri inseriti.

public class HttpErrorModule : IHttpModule
{
  public void Dispose()
  {
  }

  public void Init(HttpApplication context)
  {
    context.Error += new EventHandler(context_Error);
  }

  void context_Error(object sender, EventArgs e)
  {
    HttpErrorConfigurationElement config =
      (HttpErrorConfigurationElement)
      System.Configuration.ConfigurationManager.GetSection("HttpError");

    bool userIsAllowed = false;

    foreach (string role in config.Roles.Split(','))
      if (System.Web.HttpContext.Current.User.IsInRole(role.Trim()))
        userIsAllowed = true;

    foreach (string ip in config.IP.Split(','))
      if (System.Web.HttpContext.Current.Request.UserHostAddress ==
        ip.Trim())
        userIsAllowed = true;

    if (userIsAllowed && !String.IsNullOrEmpty(config.FullErrorPage))
      System.Web.HttpContext.Current.Response.Redirect(
        config.FullErrorPage);
  }
}

Nel caso del codice in esame, si interviene nella fase di errore. L'handler che gestisce l'errore è abbastanza banale; innanzitutto viene recuperata la sezione di configurazione dal web.config, tramite l'API System.Configuration.ConfigurationManager.GetSection, ed una volta ottenuti i dati si controlla che l'utente appartenga ai ruoli o IP specificati. Nel caso in cui almeno una di queste condizioni si verifichi, se è stata specificata una pagina di errore, allora si viene reindirizzati lì, altrimenti la pipeline prosegue la sua esecuzione e l'utente si ritrova sulla pagina relativa ai valori della sezione customErrors del web.config.

3 pagine in totale: 1 2 3

Attenzione: Questo articolo contiene un allegato.

Contenuti dell'articolo

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Approfondimenti