ASP.NET MVC–Recibir contenido en mayúsculas

Publicado 25/4/2014 12:57 por Eduard Tomàs i Avellana

Imagina que estás desarrollando un proyecto, con ASP.NET MVC y cuando llevas digamos unas, no sé, cincuenta pantallas, todas llenas con sus formularios, algunos que hacen peticiones AJAX, otros que no… bueno, imagina que cuando llevas ya bastantes vistas hechas, aparece un nuevo requisito, de aquellos que están agazapados, esperando el momento propicio para saltate a la yugular: Todos los datos que entre el usuario, deben ser guardados y mostrados en mayúsculas.

Hay varias técnicas que puedes empezar a usar: Podríamos usar text-transform de CSS para transformar lo que ve el usuario en mayúsculas. Pero eso no sirve porque los datos que el servidor recibiría serían tal y como los ha entrado el usuario (text-transform cambia solo la representación pero no el propio texto).

Otra opción, claro está, es modificar todos los viewmodels porque al enlazar las propiedades las conviertan en mayúsculas. Así, si el usuario entra “edu” en la propiedad Name de un viewmodel, el viewmodel lo convierte a EDU y listos. Pero claro… si tienes muchos viewmodels ya hechos, eso puede ser muy tedioso. Y si no los tienes, pero vas a tenerlos, pues también…

En este dilema estaba un compañero de curro, cuando me planteó exactamente dicho problema. La cuestión era si había una manera que no implicase tener que tocar todos los viewmodels, para asegurar que recibíamos los datos siempre en mayúsculas. Y por suerte, la hay.

Si conoces un poco como funciona ASP.NET MVC internamente, sabrás que hay dos grupos de objetos que cooperan entre ellos para traducir los datos de la petición HTTP a los parámetros de la acción del controlador (que suele ser un viewmodel en el caso de una acción invocada via POST). Esos dos grupos de objetos son los value providers y los model binders.

La idea es muy sencilla: Los value providers recogen los datos de la petición HTTP y los dejan en un “saco común”. Los model binders recogen los datos de ese “saco común” y con esos datos recrean los valores de los parámetros de los controladores. De esa manera los value providers tan solo deben entender de la petición HTTP y los model binders solo deben entender como recrear los parámetros del controlador. Separación de responsabilidades.

Hay varios value providers porque cada uno de ellos se encarga de una parte de la petición HTTP. Así uno se encarga de los datos en la query string, otro de los form values (datos en el body usando application/x-www-form-urlencoded), otro de los datos en json… Y hay varios model binders, porque clases específicas pueden tener necesidades específicas de conversión. Aunque el DefaultModelBinder que viene de serie puede con casi todo, a veces es necesario crearse un model binder propio para suportar algunos escenarios.

En este caso la solución pasa por usar un value provider nuevo. Un value provider que recogerá los datos de la petición HTTP y los convertirá a maýsuculas antes de enviarlos a los model binders. Con eso los model binders recibirán los datos ya en mayúsculas, como si siempre hubiesen estado así. Solucionado el problema.

Veamos el código brevemente.

Lo primero es crear la factoría que cree el nuevo value provider. Los value providers se crean y se eliminan a cada petición, y el responsable de hacerlo es una factoría, que es el único objeto que existe durante todo el ciclo de vida de la aplicación:

  1. public class ToUpperValueProviderFactory : ValueProviderFactory
  2. {
  3.     private readonly ValueProviderFactory _originalFactory;
  4.     public ToUpperValueProviderFactory(ValueProviderFactory originalFactory)
  5.     {
  6.         _originalFactory = originalFactory;
  7.     }
  8.     public override IValueProvider GetValueProvider(ControllerContext controllerContext)
  9.     {
  10.         var provider = _originalFactory.GetValueProvider(controllerContext);
  11.         return provider != null ? new ToUpperProvider(provider) : null;
  12.     }
  13. }

La idea es muy sencilla: dicha factoría delega en la factoría original para obtener el value provider. Y luego devuelve un ToUpperProvider que va a ser nuestro value provider propio:

  1. public class ToUpperProvider : IValueProvider
  2. {
  3.     private readonly IValueProvider _realProvider;
  4.     public ToUpperProvider(IValueProvider realProvider)
  5.     {
  6.         _realProvider = realProvider;
  7.     }
  8.     public bool ContainsPrefix(string prefix)
  9.     {
  10.         return _realProvider.ContainsPrefix(prefix);
  11.     }
  12.     public ValueProviderResult GetValue(string key)
  13.     {
  14.         var result = _realProvider.GetValue(key);
  15.         if (result == null)
  16.         {
  17.             return null;
  18.         }
  19.         var rawString = result.RawValue as string;
  20.         if (rawString != null)
  21.         {
  22.             return new ValueProviderResult(rawString.ToUpperInvariant(),
  23.                 result.AttemptedValue.ToUpperInvariant(),
  24.                 result.Culture);
  25.         }
  26.         var rawStrings = result.RawValue as string[];
  27.         if (rawStrings != null)
  28.         {
  29.             return new ValueProviderResult(
  30.                 rawStrings.Select(s => s != null ? s.ToUpperInvariant() : null).ToArray(),
  31.                 result.AttemptedValue.ToUpperInvariant(),
  32.                 result.Culture);
  33.         }
  34.         return result;
  35.     }
  36. }

La clase ToUpperProvider implementa la interfaz IValueProvider, pero delega en el value provider original.

Lo único que hace es, en el método GetValue, una vez que tiene el valor original obtenido por el value provider original convertirlo a mayúsculas. Tratamos dos casuísticas: que el valor sea una cadena o un array de cadenas.

¡Y listos!

El último paso es configurar ASP.NET MVC. P. ej. para hacer que todos los datos enviados via POST usando form data (es decir, un submit de form estándard) se reciban en mayúsculas basta con hacer (en el Application_Start de Global.asax.cs):

  1. var old = ValueProviderFactories.Factories.OfType<FormValueProviderFactory>().FirstOrDefault();
  2. if (old != null)
  3. {
  4.     ValueProviderFactories.Factories.Remove(old);
  5.     ValueProviderFactories.Factories.Add(new ToUpperValueProviderFactory(old));
  6. }

Con eso sustituimos la factoría original que devuelve el value provider que se encarga de los datos en el form data por nuestra propia factoría.

Para probarlo basta con crear una vista con un formulario y hacer un post normal y corriente… y verás que todos los datos que se entren se pasarán a mayúsculas automáticamente :)

Saludos!

Archivado en:
Comparte este post:

Comentarios

# re: ASP.NET MVC–Recibir contenido en mayúsculas

Saturday, May 3, 2014 12:56 AM by Juan Irigoyen

Excelente artículo Eduard, se me ocurre que incluso que podría ser un buen método para la internacionalización de aplicaciones.

Un saludo.