Gestire la culture per i parametri in querystring con ASP.NET MVC

di Marco De Sanctis, in ASP.NET 4.0, ASP.NET MVC,

Come ben sappiamo, in linea generale una action di ASP.NET MVC può accettare qualsiasi tipo di parametro. Per esempio, nel caso in cui dobbiamo gestire una richiesta in cui ci viene fornito un'intervallo di date, è assolutamente possibile scrivere un metodo di questo tipo:

public ActionResult Search(DateTime from, DateTime to)
{
  // .. codice qui ..
}

E' poi compito del framework, e nello specifico dell'accoppiata value provider e model binder, trasformare il dato proveniente dal browser nei due oggetti DateTime; ciò avviene recuperando le informazioni dal contenuto della form, nel caso in cui la richiesta sia di tipo POST, o dalla querystring se ci troviamo al cospetto di una chiamata di tipo GET.

Quando i parametri sono sensibili alla culture dell'utente (come nel caso del tipo DateTime), bisogna prestare particolare attenzione al formato in cui vengono inviati, perché il comportamento del framework varia sensibilmente a seconda della tipologia della richiesta. Infatti, se in caso di POST, il parse avviene in base alla cultura dell'utente, quando la richiesta è in GET viene forzata la InvariantCulture, che prevede per le date il formato americano. Questa scelta ha una ragione di fondo, ossia che gli URL devono essere invarianti rispetto alla culture dell'utente che li genera, e pertanto è consigliabile utilizzare sempre una sola culture per interpretare le richieste.

Purtroppo, però, l'uso della invariant culture può creare problemi nel caso in cui generiamo un URL dall'input dell'utente che, verosimilmente, sarà in formato italiano. Per esempio, immaginiamo di invocare la action vista in precedenza con la seguente form:

@using (Html.BeginForm("Search", null, FormMethod.Get))
{
  <span>Data da:</span>
  @Html.TextBox("from")<br />
  <span>Data a:</span>
  @Html.TextBox("to")<br />
    
  <input type="submit" value="Cerca" />
}

Come possiamo notare, questa form è inviata in GET con i due parametri from e to in querystring e, in condizioni normali, trasformati in DateTime secondo la invariant culture. Il risultato sarà che, se per esempio inseriamo la data "10/07/2012", questa sarà interpretata erroneamente:


Per ovviare a questo problema, possiamo creare un value provider personalizzato. Si tratta di un'operazione assolutamente banale, perchè tutta la logica di parsing è contenuta nella classe base NameValueCollectionValueProvider, e quindi possiamo limitarci semplicemente a specificare in maniera esplicita la culture da utilizzare.

internal class CultureQueryStringValueProvider : NameValueCollectionValueProvider
{
    public CultureQueryStringValueProvider(
        ControllerContext context, CultureInfo culture)
        : base(context.HttpContext.Request.QueryString, culture)
    { }
}

Questo oggetto accetta nel costruttore un parametro di tipo CultureInfo, che sarà fornito dall'oggetto CultureQueryStringValueProviderFactory, responsabile di istanziarlo:

internal class CultureQueryStringValueProviderFactory : ValueProviderFactory
{
    private CultureInfo _culture;
    public CultureQueryStringValueProviderFactory(CultureInfo culture)
    {
        if (culture == null)
            throw new ArgumentNullException("culture");

        _culture = culture;
    }

    public override IValueProvider GetValueProvider(
        ControllerContext controllerContext)
    {
        return new CultureQueryStringValueProvider(controllerContext, _culture);
    }
}

A questo punto non ci resta che l'ultimo passo, ossia sostituire, nel Global.asax, la nostra factory personalizzata a quella di default di ASP.NET MVC, specificando che vogliamo usare la culture italiana:

protected void Application_Start()
{
  // rimuovo il value provider factory di default
  var toRemove = ValueProviderFactories.Factories
    .OfType<QueryStringValueProviderFactory>()
    .Single();
  ValueProviderFactories.Factories.Remove(toRemove);

  // aggiungo il value provider custom
  var culture = CultureInfo.GetCultureInfo("it-IT");
  ValueProviderFactories.Factories.Add(
    new CultureQueryStringValueProviderFactory(culture));
}

Se ora proviamo a ricaricare la pagina precedente, essa mostrerà le date interpretate correttamente.

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

I più letti di oggi