Web Application Development – Web Communication

28 aprile 2012

Dopo moltissimo tempo torno a scrivere! Questa volta ho un preciso obiettivo. Molti dei post che pubblicherò d’ora in poi potranno sembrare scontati e semplici per qualcuno mentre per me rappresentano dei veri e propri appunti, a breve dovrò sostenere molti esami che mi permetteranno di ricevere diverse certificazioni in ambiente Microsoft, per questo motivo ho deciso di riportare qui un riassunto di ripassi/nuove conoscenze/curiosità/dubbi!

Oggi si parte con una panoramica delle attività svolte tra Client e Server per quanto concerne il funzionamento di una web application. Capire cosa accade durante la navigazione all’interno di un sito web è importante per poter meglio comprendere le mimiche che si nascondono dietro l’interazione con un server IIS. Un Web Browser (Client) ed un Web Server (Server) comunicano tra di loro utilizzando l’HTTP (Hypertext Transfer Protocol) che è un protocollo di rete assegnato alla porta 80, se il server è provvisto di un certificato, la comunicazione può avvenire su porta 443, in questo caso parliamo di HTTPS.

Tipicamente la comunicazione Client/Server prevede diverse operazioni:

  1. L’utente si collega al sito www.sample.com
  2. Il Browser attiva una connessione HTTP con www.sample.com ed inoltra un comando GET, questo comando tenta di accedere al path / (root del sito web) per cui, generalmente, è specificata una pagina di default da caricare. Tecnicamente il comando inoltrato assomiglia a questo:
    GET /
    host http://www.sample.com
  3. Il web server restituisce una risposta preceduta da un HTTP STATUS CODE che ne indica il tipo di contenuto (200 se è stata trovata una pagina web da visualizzare, 404 se la pagina non esiste, 302 se è stato effettuato un inoltro presso un’altra pagina etc. per ulteriori info sugli status code: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).

Volendo entrare nel merito delle attività, possiamo suddividere il lavoro svolto tra Client e Server, il client esegue una serie di operazioni volte alla preparazione ed inoltro/ricezione della richiesta al web server:

  1. Inoltra la richiesta al web server: se, come nell’esempio precedente, dobbiamo accedere a www.sample.com, il browser risolve l’indirizzo ed inoltra la chiamata per la richiesta della pagina.
  2. Autenticazione: se è attiva una connessione HTTPS, la richiesta deve contenere le informazioni necessarie per poter poi essere autenticata dal server.
  3. Elaborazione della risposta: quando il web server restituisce l’output della richiesta il web browser si occupa dell’elaborazione della risposta (es. se il web server ha restituito la pagina, il browser provvede a tradurla in elementi grafici, se invece sono restituiti particolari HTTP STATUS CODE, il web server si comporta nel modo più appropriato per poterli gestire).
  4. Esecuzione di codice Client Side (Scripts): se all’interno della pagina è presente del codice javascript, il browser si fa carico della sua esecuzione.

Questo processo è ripetuto ogni qual volta l’utente clicca su un bottone o seleziona un link. Il Server Web, come è facile immaginare, fa molto più del client. Una serie di processi sono innescati quando una richiesta HTTP arriva, tali processi, in parte operazioni di validazione, in parte comportamenti di contesto, concorrono alla preparazione dell’output finale da inoltrare al client. Quando una richiesta arriva sono eseguite diverse attività:

  1. Validazione: il server verifica che la richiesta sia correttamente strutturata, tale operazione si rende necessaria per evitare l’introduzione di richieste malformate che possono compromettere il corretto funzionamento del web server.
  2. Autenticazione: se la richiesta è in HTTPS (es. https://www.sample.com), è necessario autenticare la chiamata (il tutto avviene utilizzando il Secure Sockets Layer SSL).
  3. Autenticazione dell’utente: se è previsto un particolare meccanismo di autenticazione per l’utente, è necessario applicarlo, nel caso non siano presenti le credenziali necessarie per poter accedere, è visualizzata una maschera di login.
  4. Impostazione delle autorizzazioni dell’utente: successivamente è necessario impostare le autorizzazioni dell’utente, una serie di informazioni che indicano quali sono i permessi disponibili per il profilo connesso.
  5. Determinare come gestire la richiesta: a questo punto il server web deve capire in che modo interpretare ed eseguire la richiesta, se per esempio l’utente ha richiesto una pagina statica provvederà a restituirne il contenuto, se ha chiesto una pagina ASPX provvederà a servirla nel modo corretto.
  6. Gestione degli errori: se si verificano eventuali errori nel determinare il corretto percorso della richiesta, il server web cerca di fornire un dettaglio chiaro dell’errore che si è verificato (es. un Internal Server Error solitamente restituisce uno status code 500).
  7. Cache Output: il web server, provvede ad applicare anche meccanismi di cache con cui è possibile migliorare i tempi di risposta.
  8. Logging: per finire, tutte le operazioni eseguite per la chiamata, generalmente vengono loggate per questioni di sicurezza/monitoring.

La comunicazione descritta tra client e server è gestita mediante l’utilizzo di una richiesta. In ASP.NET è disponibile un oggetto, appunto Request, in cui sono raccolte tutte le informazioni che girano intorno a questa informazione. L’oggetto richiesta contiene informazioni sui Cookies, la cache, la scadenza delle pagine e molto altro. Tecnicamente parlando, il web server restituisce una risposta del genere:

HTTP/1.1 200 OK
Server: Microsoft-IIS/7.0
Content-Type: text/html
Content-Length: 154
<html><head><title>Hello World Sample</title></head><body><p>Hello World Sample Page!</p></body></html>
  • la prima linea indica il protocollo di comunicazione e le informazioni di stato (HTTP STATUS CODE).
  • la seconda linea contiene le informazioni sul tipo di web server che ha generato la risposta (Microsoft-IIS/7.0).
  • la terza linea (Content-Type) indica il tipo di contenuto erogato dal web server. Questo indicatore è anche conosciuto come MIME TYPE (Multipurpose Internet Mail Extensions). In questo esempio il MIME indica che il tipo di file è HTML.
  • la linea successiva contiene informazioni sulla grandezza effettiva del dato restituito.
  • ciò che è presente dopo è il dato vero e proprio che verrà trattato dal web browser in base alle specifiche del Content-Type.

Per ora può bastare, nel prossimo articolo: FORMS, POSTBACK e gestione degli HTTP METHODS.

ASP.NET Application State

3 agosto 2010

Quando usarla? Quando è più giusto scegliere questo contenitore mettendo da parte la cache? Prima di rispondere a questa domanda è necessaria una breve introduzione sull’argomento. L’Application State è un contenitore di dati accessibile da tutte le classi presenti all’interno di un’applicazione web. Diversamente dalle sessioni, la cui accessibilità e ristretta all’utente che ne è proprietario, l’Application State è accessibile a tutti gli utenti (proprio come la cache). Per tale motivo questo contenitore si presta bene per la memorizzazione di piccole quantità di dati spesso richieste all’interno dell’intero ciclo di vita dell’applicazione.

L’Application State viene fornito tramite un istanza di HttpApplicationState che espone un dizionario di key(string)/value(object). Questo oggetto viene istanziato la prima volta quando un qualsiasi utente si collega all’applicazione web ed è successivamente disponibile per tutto il ciclo di vita dell’applicazione stessa.

Generalmente l’accesso gestito tramite l’HttpContext (HttpContext.Current.Application) da cui possiamo inserire, modificare e cancellare valori. Le modalità con cui effettuare questa operazione viene effettuata necessitano particolare attenzione, a differenza della cache, in cui basta semplicemente richiamare il metodo Add o Insert per memorizzare valori, nell’Application State è necessario isolare il contenitore di dati per averne l’accesso esclusivo essendo questa una risorsa globale. Nell’esempio viene chiarito il concetto:

DataTable data = database.GetData();
HttpContext.Current.Application.Lock();
HttpContext.Current.Application.Add("Data", data);
HttpContext.Current.Application.UnLock();

[ad#ad-lungo]

Come potete notare, ho reso più visibile la porzione di codice prima e dopo l’inserimento, fondamentale se vogliamo assicurare che questo contenitore funzioni bene. Prima di utilizzare questo strumento è necessario conoscere alcune informazioni preliminari:

  • Essendo questa una risorsa incorporata permette di ottenere prestazioni ottimali (molto più veloci del salvataggio dei dati su database o file system). Se però la quantità dei dati memorizzati è molto grande, viene esaurita anche la memoria a disposizione del server web producendo più problemi che benefici, quindi se la quantità di dati da salvare è molto grande sarebbe opportuno pensare ad una soluzione alternativa come la cache.
  • I dati memorizzati in questo contenitore sono Volatili, ciò significa che se l’applicazione web viene riavviata o terminata per diverse motivazioni (modifiche al web.config, crash etc.) i dati vengono persi. Si deduce che non è una buona pratica affidarsi alla completa memorizzazione di informazioni nel contenitore Application ma è opportuno affidarsi anche alla gestione di questi dati in una memoria non volatile da cui poterli successivamente ripristinare.
  • Non è una soluzione adatta alla scalabilità: l’Application State non viene condiviso se stiamo lavorando in una web-farm o scenari simili (server multiprocessori, macchine virtuali etc.), quindi in questi casi è opportuno pensare ad un’alternativa più efficiente (come una vera e propria base dati e quindi l’utilizzo di un server di database) che possa assicurare il recupero dei dati in ogni momento.

Fatte queste premesse, penso sia possibile valutare quando e come introdurre l’Application State nelle proprie applicazioni!

ASP.NET MVC – ViewData vs TempData

30 luglio 2010

Questo Framework mette a disposizione diverse funzionalità, una delle tante, che ritengo davvero molto utile, è la possibilità di far transitare dati tra diverse richieste http, questa possibilità si materializza attraverso l’utilizzo di diverse proprietà all’interno della pagina web: ViewData e TempData. Non nascondo la mia iniziale confusione nel capire a cosa servissero due variabili che hanno quasi lo stesso nome, ma poi tutto ha avuto un senso più chiaro e logico!

In due parole: TempData è come ViewData con una sola differenza: TempData può contenere informazioni che vengono conservate per le (massimo) due richieste successive, oltre, il suo contenuto viene automaticamente distrutto. Quindi può essere utilizzata per passare messaggi di errore e roba del genere tra diverse Action, mentre ViewData permette un interazione diretta tra Controller e View.

un mio amico ha chiarito ancor meglio il concetto: in parole povere, il tempdata permette di passare dati tra controller mentre il viewdata è adibito a quelle logiche che restano racchiuse nella stretta corrispondenza view-cotroller (yetanothergeek).

Un esempio concreto può chiarire il concetto, supponiamo di dover implementare due diversi comportamenti:

  • Un login.
  • Il caricamento di dati all’interno di DropDownList presenti in una View.

Per quanto riguarda il Login supponiamo di avere un UserController che implementa diverse view tra le quali c’è appunto Login:

public class UserController {
    ...
    public ActionResult Login(string username, string password) {
	if(!IFormProvider.Authenticate(username, password))
             TempData["LoginErrors"] = "Autenticazione fallita.";
	return RedirectToAction("Index", "Home");
    }
}

La view non è fisicamente presente ma si occupa di verificare che username e password inseriti siano validi, nel caso in cui questa condizione non si verifichi, viene caricato un valore in TempData, LoginErrors, che contiene un avviso per l’utente, successivamente viene effettuato un RedirectToAction che realmente equivale ad un Response.Redirect, se avessimo utilizzato ViewData per memorizzare questo messaggio di errore, la Redirect non ci avrebbe permesso di memorizzare l’avviso per l’utente, TempData invece mantiene lo stato dell’informazioni per la prossima richiesta e se necessario anche per la successiva! Quindi è chiaro che sarà utile per noi sfruttare questo contenitore quando sappiamo di dover spostarci da una Action ad un’altra.

Ora passiamo al secondo esempio in cui è necessario passare dal Controller, informazioni aggiuntive alla View (supponendo di avere già una View con un Model fortemente tipizzato), sempre in UserController abbiamo un’altra view che chiamiamo List, in questa, sono presenti l’elenco completo degli utenti ed un filtro di ricerca con possibilità di filtrare per Ruolo, quindi, oltre a fornire l’elenco degli utenti trovati, dobbiamo fornire un elenco di ruoli che devono essere caricati nella DropDownList:

public class UserController {
    private DatabaseDataContext db = new DatabaseDataContext();
    ...
    private void BindRequiredData() {
	// Elenco completo dei ruoli
	ViewData["Roles"] = new SelectList(db.Roles, "RoleID", "Name");
    }

    [HttpGet]
    public ActionResult List() {
        BindRequiredData();
    }

    [HttpPost]
    public ActionResult List(int RoleID) {
	BindRequiredData();

        // Prelevo gli utenti.
        var users = db.Users.Where(u => u.RoleID == RoleID);

        return View(users.ToList());
    }
}

[ad#ad-lungo]

Ho implementato ambedue le viste, GET e POST, ho preparato un metodo privato che provvede a caricare in ViewData l’elenco dei Ruoli, successivamente nella pagina web posso sfruttare questa informazioni per la generazione della DropDownList. In questo caso si nota come l’utilizzo di ViewData è più adatto al contesto, il suo ciclo di vita equivale a quello della richiesta che, una volta completata, rilascerà ogni risorsa utilizzata.

Condivido queste informazioni in quanto io per prima mi sono ritrovato ad avere falsi problemi di ViewData non funzionante, pretendevo di poter trasmettere messaggi tra diverse Action utilizzando direttamente questo contenitore, poi scoprendo TempData ed analizzando analogie e differenze, tutto è risultato molto più chiaro!

Progetti ASP.NET MVC 2.0 – Default.aspx mancante, come risolvere?

23 giugno 2010

Chi ha lavorato con progetti ASP.NET MVC 1.0 e successivamente è passato alla versione 2.0, avrà sicuramente notato la mancanza (quando viene creato un nuovo progetto) della pagina principale nella ROOT dell’applicazione, sto proprio parlando del file Default.aspx con relativo Designer e *.cs . Cosa si dice a riguardo sulla rete?

Viene spiegato che la gestione del Redirect è mantenuta direttamente dalla Pipeline di Internet Information Service 7 (IIS 7) ove questo sia installato.

Ciò significa che se pubblichiamo la nostra applicazione su un hosting in cui IIS 7 non è presente ci ritroveremo a dover gestire errori molto semplici del tipo Accesso negato (Errore 403), “e giustamente!” io direi! Cercando di accedere ad un indirizzo http://www.pippo.com, non essendoci un servizio preposto al Routing automatico, riceviamo questo messaggio in quanto l’accesso diretto alla cartella ROOT del sito ci è negato!

La soluzione è più semplice di quanto si possa pensare. Dobbiamo semplicemente imparare da MVC 1.0 alcune cose che ci tornano utili nella versione 2. Sto parlando di ricreare semplicemente questo file, Default.aspx, con le stesse caratteristiche che riporta nella versione precedente. Procediamo subito aggiungendo al nostro progetto una nuova pagina (scusate la ripetizione, ma la chiarezza è fondamentale, la pagina dovrà chiamarsi Default.aspx).

Apriamo Default.aspx e cancelliamo tutto lasciando solo la direttiva @Page come nell’esempio:

<%@ 	Page 
	Language="C#" 
	AutoEventWireup="true" 
	CodeBehind="Default.aspx.cs" 
	Inherits="ProjectName._Default" %>

[ad#ad-lungo]

Successivamente dobbiamo intervenire sul file di codice: Default.aspx.cs

using System.Web;
using System.Web.Mvc;
using System.Web.UI;

namespace ProjectName
{
    public partial class _Default : Page
    {
        public void Page_Load(object sender, System.EventArgs e)
        {
            string originalPath = Request.Path;
            HttpContext.Current.RewritePath(Request.ApplicationPath, false);
            IHttpHandler httpHandler = new MvcHttpHandler();
            httpHandler.ProcessRequest(HttpContext.Current);
            HttpContext.Current.RewritePath(originalPath, false);
        }
    }
}

Ora siamo pronti per ripubblicare il nostro progetto per poterlo vedere funzionare senza alcun problema! Ve lo avevo detto che era davvero semplice. Chi lavora con IIS è a conoscenza del fatto che al suo interno è configurato per avere dei Default come pagine web da aprire in caso di incognita, prendo ad esempio Aruba.it che nella sua documentazione specifica l’elenco dei nomi di pagina validi per poter essere automaticamente gestiti dal web server, noi abbiamo creato un file con le stesse regole in cui successivamente viene introdotto il Routing per l’avvio del vero sito web.