Archivi tag: .Net

Usare RouteData con mvc4 per rounting a Runtime in global.asax

Spesso capita di dover eseguire dei routing dinamici all’interno delle applicazioni sviluppate con MVC3 e MVC4 della Microsoft, e molte volte non è possibile eseguire direttamente una redirect, come nel mio caso specifico, dove dovevo eseguire un redirect all’interno del Global.asax, durante la fase di Application_EndRequest,senza scatenare un loop infinito di redirect.
Ci vengono in aiuto le api dell’MVC ed in particolare la RouteData, che permette di passare tutti i dati di chiamata ad una particolare action di uno specifico Controller, instanziato programmaticamente:

1
2
3
4
5
6
7
var rd = new RouteData();
rd.DataTokens["area"] = null;
rd.Values["controller"] = "NomeController";
rd.Values["action"] = "NomeAction";

IController c = new NomeController();
c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));

Comprimere il contenuto di una risposta web, e analisi dell’Accept-Encoding

Il seguente articolo, è inerente c# e in particolare all’integrazione di un CompressionFilter nel  workflow di una chiamata web mediante il framework MVC2 di Microsoft, ma è applicabile in linea di massima a tutti i linguaggi. Si tratta fondamentalmente di applicare un algoritmo di compressione ai dati in output da una chiamata http, in modo da far viaggiare meno dati sulla rete, e accertarsi di quale metodi di compressione il client chiamante supporti.

Ci sono diversi tutorial sulla rete riguardo a questa problematica, ma è difficile trovare una risposta assoluta in quanto il vero problema non è la creazione di una funzione per la compressione (già implementate nei vari framework), ma nella decodifica corretta dell’Header Accept-Encoding che ci aiuta a dedurre che cosa supporti il chiamante, evitando così di applicare meccanismi di compressione non graditi o non supportati.

Banalmente basterebbe controllare se nell’header in questione sia presente la stringa gzip o deflate ,ad indicare che il client supporta i due algoritmi di compressione standard, ma in realtà le specifiche W3C sull’encoding, prevedono un funzionamento molto piu complesso.

 http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

Questo tipo di Headers si basano su un concetto di preferenza e pesi..

Alcuni esempi di valori possibili:

  • il più banale “gzip” oppure solo “deflate” ad indicare l’algoritmo desiderato.
  • “gzip,deflate” ad indicare la sequenza delle preferenze,
  • “gzip;q=0,deflate” ad indicare che gzip non dovrebbe minimamente essere usato (ha un peso Q pari a zero)
  • “gzip;q=0.4,deflate;q=0.4,identity;q=0.2” ad indicare la ripartizione tra le preferenze..
  • identity sta a indicare che non va assolutamente usato un content-encoding.. se espresso senza q, il default sui parametri e’ q=1 e quindi avrebbe sempre la precedenda come in questo esempio: “gzip;q=0.5,deflate;q=0.5,identity”..
  • “*” sta invece a indicare qualsiasi encoding

Da quello che si evince e’ impossibile andare a cercare semplicemente se nella stringa sia presente gzip o deflate.. perche si rischia di incappare in errori (come da esempio con lo “*” o con identity dove q=1)

Sulla rete si trovano gia delle List per valori pesati oppure realizziamo noi direttamente una semplice classe che tokenizza la stringa usando la virgola come separatore e poi tokenizza ogni token utlizzando il “;” . Ovviamente in caso di “*” la scelta di default possiamo attribuirla noi ad esempio gzip. Il sort dei token e’ eseguito sulla base del valore di q, andando a mettere q=1 a tutti gli elementi con q non specificato.

Poi passiamo alla parte MVC e realizziamo un ActionAttributeFilter che utilizzeremo per annotare le Action nel nostro Controller , che saranno sottoposte a compressione.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
using System;

using System.Collections.Generic;

using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Collections.Specialized;
using System.IO.Compression;
using System.IO;

public class CompressionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{

HttpRequestBase request = filterContext.HttpContext.Request;
string acceptEncodingHeader = request.Headers["Accept-Encoding"];
if (acceptEncodingHeader == null) return;

HttpResponseBase response = filterContext.HttpContext.Response;

MyFantasticQList encodingPreference = new MyFantasticQList(acceptEncodingHeader);

encodingPreference.setDefaultPreference("gzip");

string preferred = encodingPreference.findPreferred();

switch(preferred.Name){

case "gzip":

Response.AppendHeader("Content-Encoding""gzip");

Response.Filter = new GZipStream(Response.Filter, CompressionMode.Compress);

break;

case "deflate":

Response.AppendHeader("Content-Encoding""deflate");

Response.Filter = new DeflateStream(Response.Filter,CompressionMode.Compress);

break;

default:

break;

}

}

Realizzato il nostro CompressFilterAttribute, nel nostro controller basta annotare le action che devono restituire contenuto compresso:

 

1
2
3
4
5
6
7
8
9
[HttpPost()]

[CompressionFilter]

public ActionResult ListaElementi(string key){

//codice dell'action

}

In automatico prima della response, verrà applicato il content encoding corretto .

Ciao.

 

Ottenere il Soap Message in WCF

A volte cose molto semplici richiedono delle operazioni aggiuntive per essere espletate, e quindi cio che ci aspettiamo essere una semplice Get, prevede invece l’utilizzo di diversi pattern.

Il nostro caso:

stiamo utilizzando un client WCF per chiamare un webservice e vorremmo ottenere programmaticamente, quindi da codice, la busta Soap per memorizzarla ad esempio in sistemi di monitoring o debug particolari.

La soluzione:

Realizziamo una classe che implementi due interfacce, IClientMessageInspector e IEndpointBehavior.

Implementiamo le interfacce e otteniamo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MessageInspector : IClientMessageInspector, IEndpointBehavior

{
public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
//throw new NotImplementedException();
}
public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
Console.WriteLine("Soap Message:"+request.toString());
return null;
}

public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
//;
}

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(this);
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//;
}

public void Validate(ServiceEndpoint endpoint)
{
//;
}

}

Nel metodo BeforeSendRequest otteniamo il messaggio prima dell’invio e ad esempio lo stampiamo sulla console..

Per poter poi aggiungere questo Inspector alle caratteristiche del nostro client, basta passarlo in questo modo:

1
2
//serviceClient e' l'istanza del nostro client WCF
serviceClient.ChannelFactory.Endpoint.Behaviors.Add(new MessageInspector());

Importante l’implementazione del metodo ApplyClientBehavior , dall’interfaccia IEndpointBehavior, che ci permette di aggiungere il nostro Inspector agli Inspector del client WCF. Come tutto in WCF, anche gli inspector sono completamente configurabili nell’app.config (o web.config), nella parte di serviceModel e tramite delle extensions.

Spero di avervi (e in particolare a chi me lo ha chiesto) aiutato.