Personalizzare la client validation di ASP.NET MVC per supportare diverse culture

di Marco De Sanctis, in ASP.NET MVC,

In uno script precedente abbiamo visto come il model binder di default di ASP.NET MVC presenti già un supporto out-of-the-box alle regole di globalizzazione. Purtroppo questo non è altrettanto vero per la validazione client-side: essa, infatti, avviene sempre secondo la culture americana, a prescindere da quella dell'utente. Per far sì che il nostro sito web funzioni correttamente con tutte le culture, è necessario personalizzare la logica utilizzata da jQuery Validator per effettuare il parsing di numeri decimali.

Il modo più semplice è quello di affidarsi a jQuery Globalize, un plugin di jQuery che espone funzioni per il parsing e la formattazione di dati nelle diverse culture; l'installazione è assolutamente banale, visto che esiste un package di NuGet che contiene, in buona sostanza:

  • Uno script globalize.js, che espone i metodi e le funzioni di questa libreria;
  • Un gran numero di script globalize.culture.xx-XX.js, ognuno dei quali contiene le regole proprie di ogni culture.

Il primo passo, allora è quello di aggiungere le necessarie reference alla nostra layout page, in base alle informazioni della culture utente:

<script src="@Url.Content("~/Scripts/jquery.globalize/globalize.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.globalize/cultures/globalize.culture."  + System.Threading.Thread.CurrentThread.CurrentUICulture.ToString() + ".js")" type="text/javascript"></script>

Come possiamo notare, in particolare, il secondo link è calcolato a runtime, in base al valore della proprietà CurrentUICulture del thread corrente.

Il passo successivo, è quello di selezionare la culture da utilizzare e modificare la funzione di parsing dei number utilizzata da jQuery Validator:

<script type="text/javascript">
  $(function () {
    Globalize.culture(
      '@System.Threading.Thread.CurrentThread.CurrentUICulture.ToString()');
  });

  $.validator.methods.number = function (value, element) {
    if (value.indexOf(Globalize.culture().numberFormat[',']) > -1) {
      return false;
    }

    if (!isNaN(Globalize.parseFloat(value))) {
      return true;
    }

    return false;
  }
</script>

In pratica, dopo aver selezionato la culture desiderata, non facciamo altro che verificare che il numero digitato dall'utente possa essere correttamente interpretato dalla funzione Globalize.parseFloat(). Il primo controllo, invece, serve a considerare come invalidi gli input che contengono il separatore delle migliaia: in questo modo siamo sicuri che il numero "1.000" nella culture italiana non sia interpretato come "1000".

Con questi semplici passaggi, siamo effettivamente in grado di estendere le nostre regole di globalizzazione anche alla validazione lato client. L'unico aspetto migliorabile è che, come nel caso del model binder visto nel precedente script, l'eventuale messaggio di errore è scritto in lingua inglese. Purtroppo questa volta la soluzione non è altrettanto semplice, dato che questo messaggio è prodotto dalla classe ClientDataTypeModelValidatorProvider che non prevede alcun supporto a file di risorse esterne.

L'unica alternativa, allora, è quella di realizzarne una versione personalizzata come nel codice seguente:

public class GlobalClientDataTypeModelValidatorProvider
    : ClientDataTypeModelValidatorProvider
{
    public override IEnumerable<ModelValidator> 
      GetValidators(ModelMetadata metadata, ControllerContext context)
    {
        var result = base.GetValidators(metadata, context);

        if (result.Count() > 0)
            yield return new GlobalNumericModelValidator(metadata, context);
    }
}


L'oggetto GlobalNumericModelValidator restituito sostituisce il NumericModelValidator standard del framework, che ha il compito di generare il messaggio di errore della validazione client side di un numero.

GlobalNumericModelValidator è assolutamente identico a quello di default, tranne il fatto che utilizza il file di risorse del model binder per determinare il messaggio di errore corretto:

public class GlobalNumericModelValidator : ModelValidator
{
    public override IEnumerable<ModelClientValidationRule> 
        GetClientValidationRules()
    {
        ModelClientValidationRule rule = new ModelClientValidationRule
        {
            ValidationType = "number",
            ErrorMessage = MakeErrorString(base.Metadata.GetDisplayName())
        };
        return new ModelClientValidationRule[] { rule };
    }

    private string MakeErrorString(string displayName)
    {
        string format = this.ControllerContext.HttpContext.GetGlobalResourceObject(
            DefaultModelBinder.ResourceClassKey, "Client_PropertyValueInvalid", 
            CultureInfo.CurrentUICulture) as string;

        return string.Format(CultureInfo.CurrentCulture, format, 
            new object[] { displayName });
    }
}

L'ultimo passo da compiere consiste nel configurare il runtime affinché sfrutti questo nostro set di classi in luogo di quelle standard. Ancora una volta, il posto più opportuno per farlo è il metodo Application_Start di global.asax:

protected void Application_Start()
{
    // altro codice qui...

    var provider = ModelValidatorProviders.Providers
        .OfType<ClientDataTypeModelValidatorProvider>()
        .Single();

    ModelValidatorProviders.Providers.Remove(provider);
    ModelValidatorProviders.Providers.Add(
        new GlobalClientDataTypeModelValidatorProvider());
}

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