Hace algún tiempo escribí un post acerca de mecanismos para hacer tus servicios WebApi seguros. En este post mencionaba tres mecanismos para que tus servicios web solo sean accesibles a través de usuarios autenticados:

  • Atributo Authorize custom: no recomendado para autenticación (es para autorización).
  • Message Handler (DelegatingHandler): Mecanismo recomendado dentro de WebApi para autenticación.
  • HttpModule: Solución a nivel de IIS.

En el post comentaba que si tu aplicación depende de IIS puedes usar un HttpModule, pero que si quieres evitar tener una dependencia con él (ya que WebApi admite escenarios selfhost) debes usar un DelegatingHandler.

Pero este post está escrito antes de que OWIN fuese una realidad y con la aparición de OWIN existe otro mecanismo para autenticar nuestros servicios WebApi: usar un middleware OWIN.

Usar un middleware OWIN une las dos ventajas de los Message Handlers y los HttpModules:

  1. Al igual que un HttpModule funciona antes que WebApi por lo que autentica no solo llamadas a WebApi si no también llamadas a otros componentes (p. ej. MVC) que podamos tener.
  2. Al igual que un DelegatingHandler no está ligado a IIS en tanto que OWIN no está ligado a ningún servidor web en concreto.

El objetivo de este post es ver como crear un middleware OWIN para autenticar nuestros servicios WebApi, aunque si usáramos algún otro componente compatible con OWN (p. ej. NancyFx) nos serviría para autenticar las llamadas a ese otro componente también.

Vamos a empezar creando una aplicación ASP.NET Empty con WebApi agregado:

image

Luego agregaremos los paquetes Katana (para los despistados: Katana es una implementación de paquetes OWIN desarrollada por MS). En concreto instalaremos con NuGet el paquete Microsoft.Owin.Host.SystemWeb y este instalará ya todas las dependencias.

Creando el middleware OWIN

Un middleware OWIN es algo extraordinariamente simple. Es tan simple que no es ni una clase. Es tan solo un delegado. En su forma más básica un middleware OWIN es un Func<IDictionary<string, object>, Task>.

Aunque podríamos registrar directamente un delegado con esa firma, el sistema nos permite también registrar una clase, siempre y cuando tenga la siguiente estructura:

  1. using AppFunc = Func<IDictionary<string, object>, Task>;
  2. public class BasicAuthMiddleware
  3. {
  4.     private readonly AppFunc _next;
  5.     public BasicAuthMiddleware(AppFunc next)
  6.     {
  7.  
  8.     }
  9.  
  10.     public async Task Invoke(IDictionary<string, object> environment)
  11.     {
  12.  
  13.     }
  14. }

Este es el esqueleto básico para crear un middleware OWIN. Ahora hagamos que este middleware haga algo. Lo que hará será validar que existe autenticación básica (es decir que la cabecera HTTP Authorization llega y tiene el valor “Basic XXXX” donde XXXX es la cadena Base64 resultado de concatenar un login y un password separados por dos puntos (:).

Para ser súper genérico y no atarse a nadie, OWIN no define clases para acceder a las cabeceras HTTP, al cuerpo HTTP ni a nada. En su lugar tan solo se define el entorno: un diccionario de claves (cadena) valores (objeto). Pocas cosas hay más genéricas que eso.

¿Y qué valores tienen esas claves? Pues hay algunas que define la propia especificación, pero luego tu módulo OWIN podría añadir las que quisiera… no dejan de ser un mecanismo de comunicación entre middlewares.

Veamos ahora el código del método

El código del constructor de la clase es trivial (simplemente nos guardamos en _next el valor del parámetro que recibimos).

Veamos el código del método Invoke:

  1. public async Task Invoke(IDictionary<string, object> environment)
  2. {
  3.     var auth = GetAuthHeader(environment);
  4.     if (!string.IsNullOrEmpty(auth))
  5.     {
  6.         if (auth.StartsWith("Basic "))
  7.         {
  8.             auth = auth.Substring("Basic ".Length);
  9.             var values = DecodeAuthValue(auth);
  10.             // Validaramos login (values.Item1) y pwd (values.Item2) aqu.
  11.             // En este ejemplo suponemos que cualquier combinacin es OK
  12.             PutAuthenticationInfo(environment, values.Item1);
  13.         }
  14.     }
  15.     await _next.Invoke(environment);
  16. }

Simplemente recogemos el valor del header Authorization y si existe y tiene el formato esperado (Basic <CadenaBase64>) lo decodificamos y colocamos la identidad del usuario en el contexto de OWIN.

El método GetAuthHeader obtiene el valor de la cabecera Authorization:

  1. private string GetAuthHeader(IDictionary<string, object> environment)
  2. {
  3.     var headers = environment["owin.RequestHeaders"] as IDictionary<string, string[]>;
  4.     if (headers != null && headers.ContainsKey("Authorization"))
  5.     {
  6.         return headers["Authorization"].FirstOrDefault();
  7.     }
  8.     return null;
  9. }

Los headers se almacenan dentro del contexto en la clave owin.RequestHeaders y el valor de dicha clave es un diccionario con todos los headers. Dado que un header puede tener varios valores el diccionario es de cadena (nombre del header) a valor (array de cadenas). En este caso devolvemos tan solo el primer que haya.

El método DecodeAuthValue simplemente decodifica la cadena Base64 y devuelve una tupla con el login y el password que estaban en la cabecera:

  1. private Tuple<string, string> DecodeAuthValue(string auth)
  2. {
  3.     var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(auth));
  4.     var tokens = decoded.Split(':');
  5.     return new Tuple<string, string>(tokens[0], tokens.Length > 1 ? tokens[1] : null);
  6. }

Y finalmente el método PutAuthenticationInfo es el que crea el IPrincipal y lo coloca dentro del contexto de OWIN. Ya no debemos colocarlo en HttpContext.Current ni Thread.CurrentPrincipal. Debemos colocarlo dentro de la clave “server.User” del entorno OWIN:

  1. private void PutAuthenticationInfo(IDictionary<string, object> environment, string user)
  2. {
  3.     var claim = new Claim(ClaimTypes.Name, user);
  4.     var identity = new ClaimsIdentity(new[] {claim}, "Basic");
  5.     environment["server.User"] = new ClaimsPrincipal(identity);
  6. }

En este caso simplemente creamos un ClaimsPrincipal (con el nombre del usuario) y lo colocamos dentro del contexto.

El último punto es  configurar OWIN para que use nuestro middleware. Añade una clase, llámala Startup y coloca el siguiente código:

  1. [assembly: OwinStartup(typeof(OwinSecureDemo.Startup))]
  2.  
  3. namespace OwinSecureDemo
  4. {
  5.     public class Startup
  6.     {
  7.         public void Configuration(IAppBuilder app)
  8.         {
  9.             app.Use(typeof (BasicAuthMiddleware));
  10.         }
  11.     }
  12. }

Finalmente para probar creamos un controlador WebApi y lo decoramos con [Authorize]:

  1. [Authorize]
  2. public class BeersController : ApiController
  3. {
  4.     public IEnumerable<string> Get()
  5.     {
  6.         return new[] {"Estrella", "Voll Damm"};
  7.     }
  8. }

¡Y listos! Ya puedes probarlo pasando un valor válido a la cabecera Authorization y ver que todo funciona:

curl "http://localhost:23850/api/Beers" -H "Accept: application/json" -H "Authorization: Basic ZWl4aW1lbmlzOnB3ZA=="

Luego quitas la cabecera Authorization de la llamada y deberías recibir un error de usuario no autenticado.

Hemos visto lo sencillo que es usar un middleware OWIN para autenticación y además lo hemos implementado desde cero (sin usar ninguna clase dependiente de Katana tal como OwinContext u OwinRequest). Katana ofrece clases wrappers sobre el contexto OWIN para no tener que andar metiendo claves “a mano” en el diccionario que es el contexto de OWIN, pero eso ya sería motivo de otro post.

Un saludo!

con no comments
Archivado en: ,,

El escenario es el siguiente: tenemos un conjunto de servicios WebApi que no tienen porque estar desplegados en Azure pero queremos que esos servicios solo sean accesibles para usuarios que se hayan autenticado previamente a través de un proveedor externo (p. ej. Twitter) usando la infrastructura de Azure Mobile Services.

Creación y configuración de Mobile Services

Esa es la parte fácil… Una vez hemos creado nuestro mobile service debemos irnos a la pestaña Identity y colocar los valores que nos pide según el proveedor de autenticación externo que queramos usar. En este ejemplo usaremos twitter así que nos pide el Api Key y el Api Secret:

image

Estos valores nos lo da twitter cuando creamos una aplicación en apps.twitter.com:

image

Otro aspecto a tener en cuenta es que twitter nos pide la URL de callback, esa debe ser la URL de nuestro mobile service añadiendo el sufijo /signin-twitter.

Pero bueno… todos esos detalles están explicados fenomenalmente en la propia ayuda de Azure.

Autenticarse usando twitter a través de Mobile Services (WAMS)

El cliente de Azure Mobile Services realiza un trabajo genial a la hora de gestionar todo el flujo oAuth para autenticar a usuarios usando cualquiera de los proveedores que soporta (Facebook, twitter, Google, una cuenta de Live ID). Si creas una aplicación Windows 8.1 o bien Windows Phone, autenticarse usando un proveedor externo (twitter) a través de Mobile Services es trivial:

  1. public async void PerformAuth()
  2. {
  3.     MobileServiceClient client = new MobileServiceClient("https://beerlover.azure-mobile.net/");
  4.     var user = await client.LoginAsync(MobileServiceAuthenticationProvider.Twitter);
  5.     JwtToken = user.MobileServiceAuthenticationToken;
  6. }

Este código se encarga de gestionar todo el flujo OAuth y mostrar la UI correspondiente para que el usuario pueda introducir su login y password de twitter. Al final guarda en una propiedad JwtToken el token de autenticación retornado por WAMS.

En este punto finaliza nuestra interacción con mobile services: lo hemos usado para que el usuario se pudiese autenticar de forma fácil con un proveedor externo. Y al final obtenemos un token. Ese token no es de twitter, ese token es de WAMS y es un token JWT.

Json Web Token – JWT

JWT es un formato que define datos que puede incorporar un token que realmente es un objeto JSON. Es un formato que se está usando bastante ahora y tienes su especificación aquí.

Si ejecuto una aplicación que tenga el código anterior y me autentico usando twitter puedo ver como es el token JWT que me devuelve Mobile Services:

image

Bueno… es una ristra de carácteres bastante larga, pero básicamente se trata del objeto JSON codificado en Base64.

Para ver el contenido, puedes copiar el token y usar jwt.io para descodificar el token:

image

Podemos ver que hay tres colores en la imagen anterior y cada uno se corresponde a una parte. El primer color (verde) es un JSON con el siguiente formato:

  1. {
  2.   "typ": "JWT",
  3.   "alg": "HS256"
  4. }

Esto se conoce como JWT Envelope y nos indica el formato del token que viene a continuación (JWT) y el algoritmo usado para la firma digital (HS256).

La siguiente parte del token (azul) es realmente el JSON con los datos:

  1. {
  2.   "iss": "urn:microsoft:windows-azure:zumo",
  3.   "aud": "urn:microsoft:windows-azure:zumo",
  4.   "nbf": 1418892674,
  5.   "exp": 1421484674,
  6.   "urn:microsoft:credentials": "{\"accessToken\":\"84274067-6C7zM6rnbL5VAIF8ARIZXWg6XTZ49x67klxRTAyIU\",\"accessTokenSecret\":\"fGDwXuJg7i8rJqwJFTki5EVbEiLvgx3MlCvunOaNOm81X\"}",
  7.   "uid": "Twitter:84274067",
  8.   "ver": "2"
  9. }

Realmente cada uno de esos campos es un claim (JWT está basado en claims).

De esos claims nos interesan realmente dos. El claim iss que contiene el issuer o quien ha generado el token y el claim aud que contiene la audience que indica para quien va dirigido el token. En este caso iss nos indica que el token ha sido generado por Mobile Services y el token aud nos indica que el token es para consumo de Mobile Services. Podemos ver más claims como uid donde hay un ID de usuario.

La última parte (en rojo en la imágen) es la firma digital del token. En este caso el Envelope nos indica que la firma digital es HS256 (HMAC usando SHA256) y eso nos sirve para comprobar que el token es válido.

Securizando nuestros servicios WebApi

La idea para securizar nuestros servicios WebApi es muy simple: A cada petición de nuestros servicios miraremos si se nos pasa el token JWT en la cabcera HTTP Authentication. Si recibimos un token JWT lo parsearemos y comprobaremos la firma digital (podríamos comprobar además todos los claims que queramos). Si la firma digital es válida, el token es válido y la petición se considera que proviene de un usuario de confianza.

Para validar el token JWT vamos a usar un DelegatingHandler. Ya expliqué en un post anterior sobre como securizar servicios WebApi lo que era un DelegatingHandler.

Para la validación del token JWT nos vamos a ayudar del paquete System.IdentityModel.Tokens.Jwt así que lo primero es agregarlo a la solución. Eso sí agrega la versión 3.0.0.0 mediante el comando:

Install-Package System.IdentityModel.Tokens.Jwt -Version 3.0.0.0

Esa versión es la misma que usa internamente Mobile Services para generar el token JWT.

Así lo primero es crearte una clase (yo la he llamado JwtDelegatingHandler) que derive de DelegatingHandler y redefinir el método SendAsync:

  1. protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
  2. {
  3.     if (request.Method != HttpMethod.Options)
  4.     {
  5.         var tokenstr = RetrieveToken(request);
  6.         if (tokenstr != null)
  7.         {
  8.             var handler = new JwtSecurityTokenHandler();
  9.             if (handler.CanReadToken(tokenstr))
  10.             {
  11.                 var token = handler.ReadToken(tokenstr);
  12.                 var secret = GetSigningKey(MasterKey);
  13.                 var validationParams = new TokenValidationParameters()
  14.                 {
  15.                     SigningToken = new BinarySecretSecurityToken(secret),
  16.                     AllowedAudience = "urn:microsoft:windows-azure:zumo",
  17.                     ValidIssuer = "urn:microsoft:windows-azure:zumo"
  18.                 };
  19.                 var principal = handler.ValidateToken(tokenstr, validationParams);
  20.                 Thread.CurrentPrincipal = principal;
  21.                 if (HttpContext.Current != null)
  22.                 {
  23.                     HttpContext.Current.User = principal;
  24.                 }
  25.             }
  26.         }
  27.     }
  28.     return base.SendAsync(request, cancellationToken);
  29. }

El método usa la clase JwtSecurityTokenHandler para mirar si el token JWT es válido sintácticamente y luego llama al método ValidateToken que devuelve un ClaimsPrincipal si el token es correcto. Dicho método valida la firma digital y también comprueba que el issuer y el audence del token sean correctos. Es por ello que los establecemos a los valores que coloca WAMS.

Finalmente si no salta ninguna excepción es que el token es correcto por lo que establecemos el ClaimsPrincipal devuelto como Principal del Thread actual de forma que el atributo [Authorize] entenderá que la petición está autenticada.

El método RetrieveToken obtiene el token JWT de la cabecera:

  1. private static string RetrieveToken(HttpRequestMessage request)
  2.  {
  3.      string token = null;
  4.      IEnumerable<string> authzHeadersEnum;
  5.      bool hasHeader = request.Headers.TryGetValues("Authorization", out authzHeadersEnum);
  6.      if (!hasHeader)
  7.      {
  8.          return null;
  9.      }
  10.  
  11.      var authzHeaders = authzHeadersEnum.ToList();
  12.      if (authzHeaders.Count > 1)
  13.      {
  14.          return null;
  15.      }
  16.  
  17.      var bearerToken = authzHeaders[0];
  18.      token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
  19.      return token;
  20.  }

Y nos queda el método más importante el método GetSigninKey. Dicho método obtiene la clave que usó WAMS para firmar el mensaje y es la misma que debemos usar para comprobar la firma:

  1. internal static byte[] GetSigningKey(string secretKey)
  2. {
  3.     var bytes = new UTF8Encoding(true, true).GetBytes(secretKey);
  4.     using (SHA256Managed managed = new SHA256Managed())
  5.     {
  6.         return managed.ComputeHash(bytes);
  7.     }
  8. }

El parámetro secretKey es la “Master Key” de WAMS que puedes ver en el portal de Azure en la opción de “Manage Keys”:

image

Nota: Si buscas por Internet verás que en muchos sitios agregan la cadena JWTSig a la Master Key. No lo hagas. No sé si en versiones anteriores de WAMS era necesario, pero ahora no lo es.

Con esto ya puedes usar [Authorize] para proteger tus servicios WebApi y que solo sean válidos para aquellas peticiones que tengan un token JWT que proviene de Windows Azure Mobile Services.

Usando System.IdentityModel.Tokens.Jwt 4.0.1

Hemos visto el código necesario para validar el token JWT de WAMS usando la versión 3.0.0.0 de System.IdentityModel.Tokens.Jwt que es la que usa internamente WAMS. Pero la última versión de este paquete (y la que os instalará NuGet si no indicáis versión) es, a la hora de escribir este post, la 4.0.1.

El código no es compatible ya que hay cambios en la clase JwtSecurityTokenHandler. Si usáis la versión 4.0.1 debéis usar el siguiente código en el DelegatingHandler:

  1. var validationParams = new TokenValidationParameters()
  2. {
  3.     IssuerSigningToken = new BinarySecretSecurityToken(secret),
  4.     ValidAudience = "urn:microsoft:windows-azure:zumo",
  5.     ValidIssuer = "urn:microsoft:windows-azure:zumo"
  6. };
  7. SecurityToken outToken;
  8. var principal = handler.ValidateToken(tokenstr, validationParams, out outToken);

Este código es equivalente al anterior, podéis ver que los cambios básicos son nombres de propiedades y la firma de ValidateToken.

Usando Middleware OWIN

Comprobar que System.IdentityModel.Tokens.Jwt en su versión 4.0.1 era compatible con las firmas generadas por WAMS para los tokens JWT supone que podemos usar el middleware OWIN para validar el token JWT y olvidarnos del DelegatingHandler. Si la versión 4.0.1 no hubiese sido compatible (y algo de eso he leído en Internet, al menos en la versión 4.0.0) eso no sería posible ya que el middleware de OWIN depende de dicha versión (realmente la 4.0.0) para la validación de los tokens JWT.

Vale, para agregar el middleware OWIN basta con instalar el paquete Microsoft.Owin.Security.Jwt:

install-package Microsoft.Owin.Security.Jwt

Eso nos instalará las dependencias OWIN que tenemos, pero si no teníamos nada de OWIN instalada deberemos agregar el hosting de OWIN manualmente. Si después de este install-package no tenemos el paquete Microsoft.Owin.Host.SystemWeb deberemos instalarlo. Este paquete es el responsable de integrar el pipeline de OWIN y que se ejecute la clase de Owin Startup. El siguiente paso será crear una clase Startup:

  1. [assembly: OwinStartup(typeof(Beerlover.Server.Startup))]
  2.  
  3. namespace Beerlover.Server
  4. {
  5.     public class Startup
  6.     {
  7.         public void Configuration(IAppBuilder app)
  8.         {
  9.  
  10.             var issuer = "urn:microsoft:windows-azure:zumo";
  11.             var audience = "urn:microsoft:windows-azure:zumo";
  12.             var secret = WebConfigurationManager.AppSettings["ClientSecret"];
  13.  
  14.             var signkey = GetSigningKey(secret);
  15.             app.UseJwtBearerAuthentication(
  16.                 new JwtBearerAuthenticationOptions
  17.                 {
  18.                     AuthenticationMode = AuthenticationMode.Active,
  19.                     AllowedAudiences = new[] { audience },
  20.                     IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
  21.                     {
  22.                         new SymmetricKeyIssuerSecurityTokenProvider(issuer, signkey)
  23.                     },
  24.  
  25.                 });
  26.         }
  27.  
  28.         private byte[] GetSigningKey(string secret)
  29.         {
  30.             var bytes = new UTF8Encoding(true, true).GetBytes(secret);
  31.             using (SHA256Managed managed = new SHA256Managed())
  32.             {
  33.                 return managed.ComputeHash(bytes);
  34.             }
  35.         }
  36.     }
  37. }

Dicha clase configura el middleware de OWIN para validar tokens JWT (a través del método UseJwtBearerAuthentication). De esta manera delegamos en OWIN la validación de los tokens JWT y ya no es necesario hacer nada en WebApi,  es decir ya no debemos usar el DelegatingHandler. Por supuesto debemos seguir usando [Authorize] para indicar que servicios deben ser llamados de forma segura (con token JWT).

La solución usando OWIN es la recomendada ya que la validación de los tokens JWT se hace antes de que la petición llegue siquiera a WebApi y por lo tanto te permite integrarlo en otros middlewares (p. ej. si usas Nancy con OWIN este mismo código te sirve, mientras que el DelegatingHandler es algo propio de WebApi).

Enviando el token desde el cliente

Y simplemente por completitud del post, el código para enviar el token JWT desde el cliente sería el siguiente:

  1. protected void AddJwtToken(HttpClient client)
  2. {
  3.     client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", TwitterAuthService.Instance.JwtToken);
  4. }

Donde TwitterAuthService.Instance.JwtToken contiene el token devuelto por WAMS.

La posibilidad de delegar en WAMS todo el flujo oauth es muy cómoda y ello no nos impide que nuestra propia WebApi que puede estar en Azure (en un Website) o on-premise esté protegida a través del token JWT que emite WAMS. De esa manera la aplicación cliente tiene un solo token que usa tanto para acceder a los servicios WAMS (si los usase) como al resto de la API WebAPi que ni tiene que estar en Azure.

Espero que este post os sea de utilidad.

con no comments
Archivado en: ,,

Buenas! Vamos a ver en este post como podemos tratar la subida de ficheros en WebApi.

En ASP.NET MVC la subida de ficheros la gestiona un model binder para el tipo HttpFilePostedBase, por lo que basta con declarar un parámetro de este tipo de datos en el controlador y automáticamente recibimos el fichero subido.

En WebApi el enfoque es muy distinto: en el controlador no recibimos ningún parámetro con el contenido del fichero. En su lugar usamos la clase MultipartFormDataStreamProvider para leer el fichero subido y guardarlo en disco (ambas cosas a la vez).

Anatomía de una petición http con fichero subido

Antes de nada veamos como es una petición HTTP en la que se suba un fichero. Para ello he creado un HTML como el siguiente:

  1. <form method="post" enctype="multipart/form-data">
  2.     File: <input type="file" name="aFile"><br />
  3.     File: <input type="file" name="aFile"><br />
  4.     <input type="submit" value="Submit">
  5. </form>

Selecciono dos ficheros cualesquiera y capturo la petición generada con fiddler. El resultado (eliminando todo lo que no nos importa) es el siguiente:

  1. Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryQoYjfxGXTHG6DESL
  2.  
  3. ------WebKitFormBoundaryQoYjfxGXTHG6DESL
  4. Content-Disposition: form-data; name="aFile"; filename="jsio.png"
  5. Content-Type: image/png
  6.  
  7. Contenido binario del fichero
  8. ------WebKitFormBoundaryQoYjfxGXTHG6DESL
  9. Content-Disposition: form-data; name="aFile"; filename="logo_mvp.png"
  10. Content-Type: image/png
  11.  
  12. Contenido binario del fichero
  13. ------WebKitFormBoundaryQoYjfxGXTHG6DESL--

Básicamente:

  • El Content-Type debe ser multipart/form-data
  • El Content-Type debe especificar una boundary. La boundary es un cadena que se usa para separar cada valor de la petición (tanto los ficheros como los valores enviados por formdata si los hubiese).
  • Para cada valor:
    • Se coloca el boundary precedido de --
    • Si es un fichero.
      • se coloca un content-disposition que indica (entre otras cosas) el nombre del fichero
      • El conteido binario del fichero
    • Si no es un fichero (p. ej. es el un formdata que viene de un <input type=text>
      • se coloca un content-disposition que indica el nombre del parámetro
      • Se coloca su valor
  • Finalmente se coloca la boundary para finalizar la petición

Enviar peticiones usando HttpClient

Conociendo como es una petición de subida de ficheros, crearla usando HttpClient es muy simple. El siguiente código sube un fichero:

  1. var requestContent = new MultipartFormDataContent();
  2. var imageContent = new StreamContent(stream);
  3. imageContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png");
  4. requestContent.Add(imageContent, "image", string.Format("{0:00}.png", idx));

La variable stream es un Stream para acceder al fichero, mientras que la variable idx es un entero que en este caso se usa para dar nombre al fichero subdido (01.png, 02.png, …).

Si capturamos con fiddler como es la petición generada por este código vemos que es como sigue:

  1. POST http://localtest.me:2706/Upload/Photo/568b8c05-aab8-46db-8cbc-aec2a96dec18/2 HTTP/1.1
  2. Content-Type: multipart/form-data; boundary="c609aabb-3872-4d04-a69d-72024c9325a5"
  3. --c609aabb-3872-4d04-a69d-72024c9325a5
  4. Content-Type: image/png
  5. Content-Disposition: form-data; name=image; filename=02.png; filename*=utf-8''02.png

Podemos observar como se ha generado un boundary para nosotros (realmente el valor del boundary no se usa, es solo para separar los campos) y como se genera un Content-Disposition. Es pues una petición equivalente a usar un <input type=”file” /> (cuyo atributo name fuese “image”).

Recibir el fichero en WebApi

Para recibir el fichero subido, necesitamos una acción de un controlador WebApi y usar un MultipartFormDataStreamProvider para guardar el fichero en disco:

  1. var streamProvider = new MultipartFileStreamProvider(uploadFolder);
  2. await Request.Content.ReadAsMultipartAsync(streamProvider);

Este código ya guarda el fichero en el disco. La carpeta usada es la especificada en la variable uploadFolder. De hecho si hubiese varios ficheros subidos a la vez, este código los guarda todos.

En mi caso he enviado una petición con un Content-Disposition cuyo nombre de fichero es 02.png, así que lo suyo sería esperar que en la carpeta especificada por uploadFolder hubiese este fichero. Pero no vais a encontrar ningún fichero llamado así. Por diseño WebApi ignora el valor de Content-Disposition (por temas de seguridad). En su lugar os vais a encontrar con un fichero (o varios) llamados BodyPart y un guid:

image

Por suerte para hacer que WebApi tenga en cuenta el valor del campo Content-Disposition y guarde el fichero con el nombre especificado basta con heredar de MultipartFormDataStreamProvider y reimplementar el método GetLocalFileName:

  1. class MultipartFormDataContentDispositionStreamProvider : MultipartFormDataStreamProvider
  2. {
  3.     public MultipartFormDataContentDispositionStreamProvider(string rootPath) : base(rootPath)
  4.     {
  5.     }
  6.     public MultipartFormDataContentDispositionStreamProvider(string rootPath, int bufferSize) : base(rootPath, bufferSize)
  7.     {
  8.     }
  9.     public override string GetLocalFileName(HttpContentHeaders headers)
  10.     {
  11.         if (headers.ContentDisposition != null)
  12.         {
  13.             return headers.ContentDisposition.FileName;
  14.         }
  15.         return base.GetLocalFileName(headers);
  16.     }
  17. }

Ahora en el controlador instanciamos un objeto MultipartFormDataContentDispositionStreamProvider en lugar del MultipartFormDataStreamProvider y ahora ya se nos guardarán los ficheros con los nombres especificados. Ojo, recuerda que WebApi no hace eso por defecto por temas de seguridad, así que si implementas esta solución valida los nombres de fichero que te envía el cliente.

¡Y ya está! La verdad es que el modelo de WebApi es radicalmente distinto al de ASP.NET MVC pero igual de sencillo y efectivo ;-)

Saludos!

con no comments
Archivado en:

¡Buenas! Empiezo con esta una serie de posts, que no se lo larga que será, comparando (en el buen sentido, nada de buscar un ganador ni un perdedor) un poco Angular con React.

Antes que nada el típico disclaimer: Angular y React no cubren los mismos aspectos del desarrollo web. Sí Angular cubre todo el espectro MVC, MVVM o como quieras llamarlo, React cubre solo la V: las vistas. Es pues, de funcionalidad más limitada que Angular. Así React puede combinarse con otras librerías para obtener un framework MVC completo. Hay quien lo ha hecho con Backbone (lógico, Backbone se puede combinar con cualquier cosa) pero lo habitual es hacerlo con Flux. Pero bueno… hasta hay quien lo ha hecho con… ¡Angular!

Si no conoces nada de Angular, tranquilo que durante esta serie de posts, iremos explicando lo que sea necesario y lo mismo aplica a React. De todos modos para Angular Xavier Jorge Cerdá y Pedro Hurtado están escribiendo un tutorial en Louesfera. Échale un vistazo. De React cuesta mucho más encontrar documentación en formato tutorial en castellano…

Hello world

Por supuesto, vamos a empezar con el Hello World, un ejemplo que generalmente es tan soso que no dice nada sobre lo que se quiere analizar, pero bueno… las tradiciones son tradiciones.

Ahí va el Hello World de Angular:

  1. <!DOCTYPE html>
  2. <html ng-app="hello-app">
  3. <head lang="en">
  4.     <meta charset="UTF-8">
  5.     <title>Hello Angular</title>
  6.     <script src="bower_components/angular/angular.js"></script>
  7.     <script>
  8.           angular.module('hello-app', []).controller('HelloController',function HelloController($scope) {
  9.             $scope.name = "eiximenis";
  10.         });
  11.     </script>
  12. </head>
  13. <body>
  14.     <div ng-controller="HelloController">
  15.         Hello {{name}}
  16.     </div>
  17. </body>
  18. </html>

Al ejecutar esta página se mostrará “Hello eiximenis” por pantalla.

Es un código muy simple pero sirve para ver tres de las características fundamentales de Angular:

  1. En Angular las vistas son HTML. Es decir están formadas por el propio DOM de la página. Puede parecer obvio (HTML va muy bien para definir aspecto visual) pero bueno, hay otras librerías (p. ej. Backbone) que usan código para definir las vistas.
  2. Hay un sistema de bindings para transferir datos desde el controlador (HelloController) hacia la vista (DOM). En este caso hemos usado la sintaxis más simple, al estilo mustache.
  3. El sistema de inyección de dependencias que tiene Angular (el parámetro $scope) está inyectado automáticamente por Angular. La idea es que se pueden inyectar aquellos servicios que se desee a los controladores.

Veamos el código equivalente en React:

  1. <!DOCTYPE html>
  2. <html>
  3. <head lang="en">
  4.     <meta charset="UTF-8">
  5.     <title>Hello react</title>
  6.     <script src="bower_components/react/react.js"></script>
  7.     <script src="bower_components/react/JSXTransformer.js"></script>
  8. </head>
  9. <body id="example">
  10.     <script type="text/jsx">
  11.         /** @jsx React.DOM */
  12.         React.renderComponent(
  13.         <div>Hello, eiximenis!</div>,
  14.         document.getElementById('example')
  15.         );
  16.     </script>
  17. </body>
  18. </html>

El ejemplo es muy simple, pero luce más complejo que el equivalente de Angular. Y eso es debido a la filosofía de React:

  • React se basa en el uso de pequeños componentes que cada uno de ellos se puede renderizar independientemente (algo parecido a lo que proponen librerías como Polymer, pero con la diferencia de que Polymer se basa en Web Components y extiende el DOM, mientras que React se basa en JavaScript y se olvida del DOM).
  • Las vistas no son HTML (DOM), si no clases JavaScript. La idea es la misma que las vistas de Backbone, con la salvedad de que en Backbone se suele interaccionar con el DOM (usualmente a través de jQuery) y en React no.
  • React “extiende” JavaScript para permitir esta mezcla de html y JavaScript. Para ello se usa un parser específico, que está en el fichero JSXTransformer.js
  • No hay binding en React, porque no hay DOM hacia el que enlazar nada.
  • React tiene el concepto de “DOM Virtual”: no se interacciona nunca con el DOM real, si no con un “DOM Virtual” proporcionado por React. Luego es React quien se encarga de sincronizar este “DOM Virtual” con el DOM real del navegador. Eso es un “epic win” para SEO en SPA: react puede renderizar vistas sin necesidad de un navegador… se puede renderizar una vista React desde node p. ej. usando el mismo código en el servidor que en el cliente, y permitiendo así que los buscadores indexen nuestro sitio. Esto no es posible en Angular, ya que Angular está atado al DOM y el DOM requiere un navegador. Ojo, no digo que con Angular no se pueda generar aplicaciones SPA con buen soporte para SEO, solo digo que con React el mismo código sirve para renderizar en servidor y en cliente, mientras que con Angular la parte del servidor requiere código adicional. Además, teóricamente, este “DOM Virtual” permitiría a React generar otras cosas que no sean vistas en HTML… quien sabe.

Bueno… y para ser el primer post lo dejaremos aquí…

con 2 comment(s)
Archivado en:

Muy buenas! Seguimos ese tour de force sobre las traducciones de los mensajes de validación de Data Annotations.

En el primer post de esta serie vimos como crear adaptadores de atributos para permitirnos fácilmente y a nivel centralizado establecer las propiedades ErrorMessageResourceName y ErrorMessageResourceType.

El post terminaba con una lista de los distintos adaptadores que tiene ASP.NET MVC y que podíamos usar como clases base. Hay adaptadores definidos para casi todos los atributos de Data Annotations (Required, StringLength,…) pero no hay ninguno para el CompareAttribute. El atributo Compare no se usa mucho, ya que valida que dos propiedades tengan el mismo valor. El clásico uso es en formularios de registro donde el usuario debe introducir una contraseña dos veces para evitar que haya ningún error.

Pero el hecho de que ASP.NET MVC no incluya ningún adaptador base para dicho atributo no nos impide crearnos el nuestro y aplicar la misma técnica para traducir los mensajes de validación de dicho atributo. Para ello derivaremos de la clase DataAnnotationsModelValidator<T> (siendo T el tipo del atributo, en este caso el CompareAttribute).

La principal diferencia es que ahora debemos generar las validaciones de cliente, sobreescribiendo el método GetClientValidationRules().

Pese a todo, el código sigue siendo muy sencillo:

  1. public class LocalizedCompareAdapter : DataAnnotationsModelValidator<System.ComponentModel.DataAnnotations.CompareAttribute>
  2. {
  3.     public LocalizedCompareAdapter(ModelMetadata metadata, ControllerContext context, CompareAttribute attribute)
  4.         : base(metadata, context, attribute)
  5.     {
  6.         attribute.ErrorMessageResourceName = "Compare";
  7.         attribute.ErrorMessageResourceType = typeof(Resources.Messages);
  8.     }
  9.  
  10.     public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
  11.     {
  12.         var other = Attribute.OtherProperty;
  13.         return new[] { new ModelClientValidationEqualToRule(base.ErrorMessage, other) };
  14.     }
  15. }

En el método GetClientValidationRules devolvemos las validaciones de cliente. En este caso, queremos comparar dos propiedades así que devolvemos una ModelClientValidationEqualToRule, a la cual le pasamos el nombre de la otra propiedad. Recuerda que el [Compare] se aplica a una  propiedad (p. ej. ConfirmPassword) y se coloca el nombre de la otra propiedad (p. ej. Password):

  1. public string Password { get; set; }
  2. [Compare("Password")]
  3. public string ConfirmPassword { get; set; }

En este caso es la propiedad ConfirmPassword la que está decorada con el CompareAttribute, por lo tanto esta es la contendrá la regla de validación en cliente. De hecho el código HTML generado por los helpers Html.TextBoxFor para esas dos propiedades es:

  1. <input id="Password" name="Password" type="text" value="" />
  2. <input data-val="true" data-val-equalto="Los valores de ConfirmPassword y Password deben ser iguales"
  3.        data-val-equalto-other="Password" id="ConfirmPassword" name="ConfirmPassword" type="text" value="" />

Se puede ver que el <input /> que se corresponde a Password no tiene validaciones aplicadas y que es el <input /> que corresponde a ConfirmPassword el que tiene las validaciones de cliente aplicadas.

Podemos ver que el mensaje de error (data-val-equalto) está en castellano porque en mi fichero de recursos (Messages.es.resx) tengo la entrada “Compare”que es la que usa nuestro adaptador de recursos:

image

¡Y listos! Hemos visto como el hecho de que ASP.NET MVC  no provea un adaptador base no nos impide usar DataAnnotationsModelValidator<T> para crearnos nuestro propio adaptador, con la salvedad de que debemos indicar las validaciones de cliente a generar (las de servidor no son necesarias).

PD: Por supuesto debemos registrar ese adaptador de atributo como vimos en el post dedicado a los adaptadores:

  1. DataAnnotationsModelValidatorProvider.RegisterAdapter(
  2.     typeof (System.ComponentModel.DataAnnotations.CompareAttribute),
  3.     typeof (LocalizedCompareAdapter));

Un saludo!

con no comments
Archivado en:

En el post anterior vimos como localizar los mensajes de validación de Data Annotations en ASP.NET MVC usando adaptadores de atributos. Pero además de esos mensajes ASP.NET MVC tiene algunos mensajes de traducción implícitos.

P. ej. si tenemos una propiedad de tipo Int y le intentamos poner un valor no numérico ASP.NET MVC mostrará un mensaje de error. Este mensaje de error no proviene de Data Annotations, por lo que no podemos usar la técnica descrita en el post anterior para traducirlo.

Vamos a ver como traducir los mensajes implícitos. Para ello basta con crear un fichero de recursos (en App_GlobalResources) con la entrada PropertyValueInvalid y MVC usará dicho mensaje para mostrar el error. Puedes usar {0} para mostrar el valor inválido y {1} para mostrar el nombre de la propiedad.

La entrada PropertyValueInvalid la usa el model binder cuando se encuentra un valor inválido para una propiedad. P. ej. si tenemos una propiedad definida como int e intentamos asignarle una cadena.

Debemos configurar el model binder para que use el fichero de recursos que hemos creado usando la propiedad ResourceClassKey:

  1. DefaultModelBinder.ResourceClassKey = "Messages";

El valor de ResourceClassKey es el nombre del fichero de recursos a usar.

Traducir el mensaje implícito de propiedad requerida

El atributo [Required] de Data Annotations nos permite especificar que el valor de una propiedad es obligatorio. Pero MVC trata automáticamente algunos campos como obligatorios: las propiedades cuyo tipo es un tipo por valor son tratadas como obligatorias automáticamente. Eso es lógico: si tengo una propiedad declarada como int, es obvio que debe tener siempre un valor, ya que int no admite el valor null.

Pero el tratamiento exacto que se da en esos casos depende del valor de la propiedad AddImplicitRequiredAttributeForValueTypes de la clase DataAnnotationsModelValidatorProvider:

  • Si vale true (valor por defecto) se añade automáticamente un atributo [Required] a cada propiedad cuyo tipo sea un tipo por valor.
  • Si vale false el tratamiento lo realiza el default model binder.

Si estamos en el primer caso, eso significa que dicho mensaje realmente tenemos que tratarlo a través de un adaptador de atributo porque, a todos los efectos, es como si la propiedad tuviese un atributo [Required] colocado.  Así nos podríamos crear un adaptador para el atributo Required, tal y como se comentaba en el post anterior.

Si estamos en el segundo caso, entonces es el model binder quien tratará ese caso. Para mostrar el mensaje (que por defecto es un insulso “A value is required”) usará la entrada PropertyValueRequired del fichero de recursos que hayamos especificado mediante la propiedad ResourceClassKey.

Así vemos que el DefaultModelBinder usa dos claves del fichero de recursos:

  • PropertyValueInvalid: Cuando se asigna un valor incorrecto a una propiedad
  • PropertyValueRequired: Cuando no se asigna valor a una propiedad cuyo tipo es un tipo por valor (siempre y cuando AddImplicitRequiredAttributeForValueTypes valga false).

Nota: Esas dos son las dos únicas claves que usa el Default Model Binder.

Validación en cliente

Si tienes habilitada la validación en cliente, las cosas se ponen un poco más interesante. Hasta ahora hemos visto como traducir los mensajes implícitos que usa el Default Model Binder, pero la validación en cliente es independiente del Model Binder y es necesario un paso más.

Si la tienes habilitada verás que no te funciona… Aparecen otros mensajes de errores. P. ej. en el caso de introducir un valor no numérico en una propiedad numérica (un int p. ej.) ahora aparece un mensaje “The field xxx must be a number”.

Mirando el código fuente de la página puedes ver que este mensaje es de la validación en cliente:

image

¿Quien ha generado este mensaje y de donde lo saca?

Pues bien, estos mensajes de errores los genera la clase ClientDataTypeModelValidatorProvider que es la encargada de gestionar las validaciones en cliente.

Por suerte dicha clase expone también una propiedad ResourceCssKey, al igual que el DefaultModelBinder que podemos usar para especificarle un fichero de recursos:

  1. ClientDataTypeModelValidatorProvider.ResourceClassKey = "Messages";

Ahora la pregunta a responder es: ¿qué claves espera encontrar en este fichero de recursos?

Pues las siguientes:

  • FieldMustBeNumeric: En el caso que se entre una cadena en campos que deban ser numéricos
  • FieldMustBeDate: En el caso de que se entre una cadena incorrecta en campos que deban ser fechas.

Si te preguntas “qué es un campo numérico”, pues cualquiera cuyo tipo en la propiedad .NET equivalente sea: byte, sbyte, short, ushort, int, uint, long, ulong, float, double y decimal o las versiones Nullable de esos tipos.

Un campo fecha es aquel cuya propiedad se haya declarado como DateTime (o DateTime?) (y que no tenga aplicado el atributo [DataType] con el valor “Time”).

Por lo tanto nos basta con agregar esas dos recursos a nuestro fichero de recursos para poder traducir los mensajes implícitos.

Vale. Un apunte final: si te fijas en el código HTML para el <input /> de la propiedad Age, verás que no ha generado el data-val-required. Eso es porque tenia la línea:

  1. DataAnnotationsModelValidatorProvider.
  2.     AddImplicitRequiredAttributeForValueTypes = false;

Recuerda que eso hace que MVC no añada automáticamente un [Required]. La validación en cliente no entiende de campos requeridos si no hay un [Required]. Es por ello que no se genera el código en cliente para asegurarse que deba entrar un valor en este campo. Si elimino esa línea y ejecuto de nuevo ahora si que me aparece la validación de campo obligatorio:

image

Si te preguntas ahora de donde sale el mensaje usado para la validación de campo obligatorio en el cliente, la respuesta es que del atributo [Required] que MVC ha añadido automáticamente a la propiedad.

Por lo tanto, si usas un adaptador de atributo para el [Required] este se aplicará (y dado que se aplica también en servidor verás el mismo mensaje en el cliente que en el servidor).

En resumen…

Resumiendo, los mensajes implícitos de MVC son generados por:

  1. El DefaultModelBinder en el servidor
  2. El ClientDataTypeModelValidatorProvider en la validación en cliente

Ambos usan una propiedad (ResourceCssKey) para especificar el fichero de recursos a utilizar. Y dentro de ese fichero de recursos podemos colocar las claves:

  1. PropertyValueInvalid: Cuando se asigna un valor inválido a una propiedad. Usado por el DefaultModelBinder
  2. PropertyValueRequired: Cuando no se ha informado el valor de una propiedad que es obligatoria porque su tipo es un tipo por valor (usado por el DefaultModelBinder solo si AddImplicitRequiredAttributeForValueTypes  es false).
  3. FieldMustBeNumeric: Cuando se introduce un valor no numérico en una propiedad numérica (usada por ClientDataTypeModelValidatorProvider. El equivalente en servidor sería PropertyValueInvalid).
  4. FieldMustBeDate: Cuando se introduce un valor que no es una fecha en una propiedad de tipo fecha (usada por ClientDataTypeModelValidatorProvider. El equivalente en servidor sería PropertyValueInvalid).

Espero que te haya sido útil! En otro post iremos un paso más allá y veremos como personalizar al máximo esos mensajes de error. Pero ya te avanzo que nos meteremos hasta la cocina y el baño de ASP.NET MVC.

Saludos!

con no comments
Archivado en:

Pues sí… la verdad es que esa es una cuestión recurrente en ASP.NET MVC. Y es que con las distintas versiones de MVC han aparecido distintas maneras de conseguir este propósito.

Nota 1: Para tener una idea de como eran las cosas en MVC2 echad un vistazo al post que publicó el Maestro hace tiempo: Modificar los mensajes de validación por defecto en ASP.NET MVC 2. Por favor léete dicho post, pues en cierto modo mi post es una “continuación”.

Nota 2: Una opción rápida es instalar paquetes de idioma de MVC. Esos paquetes vienen con los mensajes ya traducidos en varios idiomas. Podemos instalar tantos paquetes de idiomas como necesitemos y dependiendo de la cultura en el hilo del servidor se usará uno u otro. Eso nos permite tener los mensajes traducidos (aunque no podremos modificarlos, son los que son). De nuevo el Maestro publicó sobre ello: Errores de ASP.NET MVC 4 en distintos idiomas

El post de José María explicar muy bien como era la situación en MVC2. Pero en MVC3 y sobretodo en MVC4 hubieron algunos cambios significativos que voy a comentar en este post.

Por supuesto podemos seguir usando la propiedad ErrorMessage de los atributos de Data Annotations. Pero eso sigue sin ser multi-idioma y además es muy pesado. Otra opción que sigue siendo válida y de hecho es la que se sigue (indirectamente) usando son las propiedades ErrorMessageResourceName y ErrorMessageResourceType.

Herencia de atributos

Jose María menciona en su post la posibilidad de usar esas propiedades a cada atributo de DataAnnotations (lo que no es muy DRY) o bien crearse un atributo de DataAnnotations derivado que auto-asigne dichas propiedades. Es decir hacer algo como lo siguiente:

  1. public class LocalizedRangeAttribute : RangeAttribute
  2. {
  3.  
  4.     public LocalizedRangeAttribute(int min, int max) : base(min, max)
  5.     {
  6.         InitProps();
  7.     }
  8.  
  9.     public LocalizedRangeAttribute(double min, double max) : base(min, max)
  10.     {
  11.         InitProps();
  12.     }
  13.  
  14.     public LocalizedRangeAttribute(Type type, string min, string max) : base(type, min, max)
  15.     {
  16.         InitProps();
  17.     }
  18.  
  19.     private void InitProps()
  20.     {
  21.         ErrorMessageResourceName = "Range";
  22.         ErrorMessageResourceType = typeof (Resources.Messages);
  23.     }
  24. }

Por supuesto me he creado mi fichero Resources.resx dentro de la carpeta App_GlobalResources (tal y como cuenta José María en su post):

image

Lamentablemente eso rompe la validación en cliente de MVC3. A modo de ejemplo tengo dicha entidad, con dos propiedades, una decorada con el Range de toda la vida y otra con mi LocalizedRange:

  1. public class Ufo
  2. {
  3.     [Range(1,90)]
  4.     public int Age { get; set; }
  5.  
  6.     [LocalizedRange(1,90)]
  7.     public int LocalizedAge { get; set; }
  8. }

Creo una vista estándar para editar objetos de este modelo:

  1. @model WebApplication1.Models.Ufo
  2.  
  3.  
  4. @Html.LabelFor(m=>m.Age)
  5. @Html.TextBoxFor(m=>m.Age)
  6. <br />
  7. @Html.LabelFor(m => m.LocalizedAge)
  8. @Html.TextBoxFor(m => m.LocalizedAge)

Si nos vamos al código fuente de la página veremos lo siguiente:

  1. <label for="Age">Age</label>
  2. <input data-val="true" data-val-number="The field Age must be a number." data-val-range="The field Age must be between 1 and 90." data-val-range-max="90" data-val-range-min="1" data-val-required="The Age field is required." id="Age" name="Age" type="text" value="" />
  3. <br />
  4. <label for="LocalizedAge">LocalizedAge</label>
  5. <input data-val="true" data-val-number="The field LocalizedAge must be a number." data-val-required="The LocalizedAge field is required." id="LocalizedAge" name="LocalizedAge" type="text" value="" />

Observa como el segundo input, que se corresponde a la propiedad LocalizedAge (decorada con mi LocalizedRangeAttribute) no tiene los atributos para validar en cliente el rango (los data-val-range-*). Por lo tanto la validación en cliente de dicho campo no funcionará.

En servidor por supuesto la validación funcionará y además se puede ver que en el segundo caso se usa el mensaje del fichero de recursos:

image

De todos aunque la herencia funcionase bien existen motivos para no usarla (p. ej. si quieres enviar a una vista una entidad de EF, deberías decorar dicha entidad con los atributos heredados, lo que no es muy bonito y no sé si puede causar efectos colaterales en el propio EF).

Adaptadores de atributos

Vale, queda claro que la herencia de atributos no funciona bien con la validación remota. Pero que no cunda el pánico, ASP.NET MVC nos da otro mecanismo: los adaptadores de atributos.

Para crear una adaptador de atributo, debemos derivar de una clase de MVC, que depende del tipo de atributo. P. ej. para crear un adaptador para el atributo de Range, debemos derivar de System.Mvc.RangeAttributeAdapter:

  1. public class LocalizedRangeAttributeAdatper : RangeAttributeAdapter
  2. {
  3.     public LocalizedRangeAttributeAdatper(ModelMetadata metadata, ControllerContext context, RangeAttribute attribute) : base(metadata, context, attribute)
  4.     {
  5.         attribute.ErrorMessageResourceName = "Range";
  6.         attribute.ErrorMessageResourceType = typeof(Resources.Messages);
  7.     }
  8. }

El adaptador recibe en su constructor al propio RangeAttribute y allí aprovechamos para establecer las propiedades ErrorMessageResourceName y ErrorMessageResourceType.

Una vez hemos creado el adaptador, debemos declararlo en ASP.NET MVC. Para ello en el Application_Start metemos el siguiente código:

  1. DataAnnotationsModelValidatorProvider.
  2.     RegisterAdapter(typeof (RangeAttribute), typeof (LocalizedRangeAttributeAdatper));

La llamada RegisterAdapter acepta dos parámetros: el tipo del atributo a adaptar y el tipo del adaptador. Una vez hecho esto, automáticamente todos los atributos Range pasarán a usar, los recursos indicados. Ya no hay necesidad ninguna del LocalizedRange.

Otros adaptadores de atributos son los siguientes (todos en el namespace System.Web.Mvc):

image

¡Espero que os haya sido útil!

Saludos!

con 3 comment(s)
Archivado en:

Buenas, un post cortito y sencillito ;)

En C# los enums son relativamente limitados: básicamente se limitan a tener un conjunto de valores y nada más. En otros lenguajes como Java o Swift, los enums pueden declarar métodos.

A priori puede parecer que no es muy necesario que un enum tenga un método, y de hecho no es algo que se suela echar en falta. Pero en algunos casos puede ser útil, especialmente para tener nuestro código más bien organizado.

P. ej. imagina un enum que contuviese los valores de los puntos cardinales:

  1. public enum FacingOrientation
  2. {
  3.     North = 0,
  4.     East = 1,
  5.     South = 2,
  6.     West = 3
  7. }

Ahora podríamos requerir un método que nos devolviese el siguiente punto cardinal, en sentido horario. Es decir si estamos mirando al norte y giramos en sentido horario, estaremos mirando al este.

Este método sería un candidato para estar en el propio enum para que así pudiese hacer tener código como el siguiente:

  1. var orientation = FacingOrientation.South;
  2. var neworientation = orientation.Turn(1);

El método Turn devolvería la nueva orientación después de N giros en sentido horario.

Como he dicho antes en C# esto no es directamente posible porque los enums no pueden contener métodos. Pero por suerte si que podemos declarar un método de extensíón sobre un enum específico:

  1. public static FacingOrientation Turn(this FacingOrientation orientation, int steps)
  2. {
  3.     var idx = (int)orientation;
  4.     idx += steps;
  5.     return (FacingOrientation)(idx % 4);
  6. }

Y el resultado es a todos los efectos casi idéntico :)

Saludos!

con 2 comment(s)
Archivado en:

Muy buenas! Un post cortito para contaros un problemilla que nos hemos encontrado en un proyecto ASP.NET MVC5. Aunque seguro que aplica a todas las versiones de MVC desde la 2 al menos.

Es uno de aquellos casos en que, evidentemente hay algo que está mal, pero a simple vista todo parece correcto. Luego das con la causa del error puede que o bien no entiendas el porqué o bien digas “¡ah claro!” dependiendo de si conoces o no como funciona el Model Binder por defecto de MVC.

Reproducción del error

Es muy sencillo. Create una clase llamada Beer tal y como sigue:

  1. public class Beer
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5.     public int BeerTypeId { get; set; }
  6. }

En una aplicación real, para editar una cerveza quizá usaríamos un viewmodel que contendría la cerveza que estamos editando y datos adicionales, p. ej. una lista con los tipos válidos para que el usuario pueda seleccionarlos de una combo:

  1. public class BeerViewModel
  2. {
  3.     public Beer Beer { get; set; }
  4.     public SelectList BeerTypes { get; set; }
  5. }

En el controlador rellenaríamos un BeerViewModel y lo mandaríamos para la vista de edición:

  1. public ActionResult Edit(int id)
  2. {
  3.     // Cargaramos la cerveza de la BBDD
  4.     var beer = new Beer() {Id = id, Name = "Beer " + id};
  5.     var model = new BeerViewModel()
  6.     {
  7.         Beer = beer,
  8.         // Cargaramos los tipos de cerveza de algn sitio
  9.         BeerTypes = new SelectList(new[]
  10.         {
  11.             new {Id = 1, Name = "Pilsen"},
  12.             new {Id = 2, Name = "Bock"},
  13.             new {Id = 3, Name = "IPA"}
  14.         }, "Id", "Name")
  15.     };
  16.     return View(model);
  17. }

La vista de edición por su parte se limita a mostrar un formulario para editar el nombre y el tipo de cerveza:

  1. @model WebApplication17.Models.BeerViewModel
  2.            
  3. <h2>Edit a Beer</h2>
  4.  
  5. @using (Html.BeginForm())
  6. {
  7.     @Html.LabelFor(m => m.Beer.Name)
  8.     @Html.TextBoxFor(m => m.Beer.Name)
  9.     <br />
  10.     <p>Choose beer type:
  11.         @Html.DropDownListFor(m => m.Beer.BeerTypeId, Model.BeerTypes)
  12.     </p>
  13.     <input type="submit" value="edit"/>
  14. }

El funcionamiento de la vista es, como era de esperar, correcto:

image

Ahora creamos la acción para recibir los datos de la cerveza y miramos que datos recibimos en el controlador:

image

¡No se ha producido el binding! Los datos que envía el navegador en el POST son correctos (no podía ser de otra forma ya que he usado los helpers para formulario):

image

¿Cuál es la causa del fallo?

Pues que el parámetro de la acción se llama “beer”. Cámbialo para que tenga otro nombre y… voilá:

image

Todos los datos enlazados (excepto el Id vale, al final lo comentamos).

¿Porque no puede mi parámetro llamarse beer?

Porque el ViewModel que estamos usando BeerViewModel tiene una propiedad con ese nombre. De hecho si cambias el nombre de la propiedad del BeerViewModel te funcionará todo de nuevo. Y eso tiene que ver en como funciona el Model Binder. Déjame que te lo cuente de forma simplificada para que tengas clara el porque eso falla.

El Model Binder es el encargado de enlazar los valores de la request con los parámetros del controlador. Los parámetros que recibe el Model Binder de la request (en el form data) son los siguientes:

Beer.Name y Beer.BeerTypeId

Cuando el Model Binder va a enlazar Beer.Name hace lo siguiente:

  • Dado que “Beer.Name” tiene un punto el model binder busca si existe algún parámetro en el controlador llamado “Beer” (case insensitive). Esto es porque un controlador puede tener varios parámetros en la acción.
    • Si lo encuentra entonces buscará una propiedad que se llame Name en dicho parámetro y la enlazará.
    • Si no lo encuentra buscará el primer parámetro posible que tenga una propiedad llamada Beer.
      • Dentro de la propiedad llamada Beer buscará una propiedad llamada Name para enlazarla.

Por eso cuando en la acción el parámetro se llamaba “beer”, el Model Binder al enlazar el parámetro “Beer.Name” intentaba enlazar la propiedad “Name” del propio parámetro. Pero esa propiedad no existe. La clase BeerViewModel solo tiene una propiedad “Beer” y otra “BeerTypes”. Lo mismo ocurre con el parámetro Beer.BeerTypeId (intenta enlazar la propiedad BeerTypeId del propio BeerViewModel).

Al final el Model Binder encuentra que no hay nada que enlazar, así que no hace nada y por eso no recibimos datos.

Cuando hemos cambiado el parámetro del controlador para que se llame “data” entonces el Model Binder al enlazar “Beer.Name” busca un parámetro llamado “beer” en la acción. Pero como NO lo encuentra, entonces busca un parámetro en el controlador que tenga una propiedad llamada “Beer”. Y lo encuentra, porque el parámetro “data” (el BeerViewModel) tiene una propiedad llamada Beer. Luego busca si el tipo de dicha propiedad (la clase Beer) tiene una propiedad llamada Name. Y la encuentra, y la enlaza.

Por eso en el segundo caso recibimos los datos.

Bonus track: ¿Por qué el Id no se enlaza? Esa es sencilla: porque el route value se llama “id” (el POST está hecho a /Beers/Edit/{id}. El Model Binder soporta enlazado de route values, pero el nombre id no lo puede enlazar porque:

  1. No existe ningún parámetro llamado Id en la acción
  2. Ningún parámetro de la acción tiene una propiedad llamada Id.

Espero este post os haya sido interesante y si alguna vez os pasa eso… pues bueno, ya sabéis la razón! :D

Saludos!

con no comments
Archivado en:

Hace algunos días Juan Quijano escribió un post en GenBetaDev con este mismo título donde comentaba lo poco que le gustaba que la funciones devolviesen null y lo que hacía para evitar errores en ese caso.

Este post es mi respuesta a su post, ya que personalmente no me gusta la solución que presenta. En general termina con una solución como la siguiente:

  1. public class Modelo
  2. {
  3.     public Persona GetPersonaByName(string nombre)
  4.     {
  5.         Persona persona = new Persona();
  6.         if (nombre == "pepe")
  7.         { persona = new Persona { Nombre = nombre, Edad = 14 }; }
  8.         return persona;
  9.     }
  10. }
  11. public class Persona
  12. {
  13.     public string Nombre { get; set; }
  14.     public int Edad { get; set; }
  15.     public Persona()
  16.     {
  17.         Nombre = string.Empty;
  18.         Edad = 0;
  19.     }
  20. }

No me gusta por varias razones, la principal es que “oculta” la causa, devolviendo una Persona “sin datos” cuando no se encuentra la persona. Está estableciendo una convención que nadie más sabe: que una persona con el nombre vacío “no existe” en realidad. El código va a terminar llenándose de ifs para validar si el nombre es o no vacío para hacer algo o no. A diferencia de un null, donde olvidarte del if genera un error en ejecución (y por lo tanto es visible), dejarte un if en este caso hará que tu código se comporte mal… y a veces esto puede ser mucho, pero que mucho, más difícil que detectar el null reference, que al menos viene con stack trace. Otro problema es que no siempre existe en todo el rango de valores posibles un valor que pueda ser usado como “indicador de que no hay datos”.

Hay varios patrones para tratar esos caso, el más conocido el NullObject. De hecho un NullObject “mal hecho” es lo que propone Juan en su post. No soy muy amante del NullObject, aunque lo he usado a veces (la última hace poco en un refactoring, donde se tuvo que “desactivar” toda una funcionalidad. En este caso lo hicimos creando un NullObject del objeto que se estaba usando, de forma que el impacto en el resto del código (unos 90 proyectos de VS) fue nulo).

No quiero hablar del NullObject, si no presentar otra alternativa. En este caso una clase que contenga el valor más un indicador de si el valor existe o no. Vamos lo equivalente a Nullable<T> pero para cualquier tipo (sí… incluso los que pueden ser null). A priori parece que no ganamos nada pero dejadme un rato y veréis las ventajas que aporta.

La versión inicial de nuestra clase sería:

  1. public struct Maybe<T>
  2. {
  3.  
  4.     private readonly T _value;
  5.     private readonly bool _isEmpty;
  6.     private readonly bool _initialized;
  7.  
  8.     public T Value
  9.     {
  10.         get { return _value; }
  11.     }
  12.  
  13.     public bool IsEmpty
  14.     {
  15.         get { return (!_initialized) || _isEmpty; }
  16.     }
  17.  
  18.     public Maybe(T value)
  19.     {
  20.         _value = value;
  21.         _isEmpty = ((object)value) == null;
  22.         _initialized = true;
  23.     }
  24.  
  25.     public static Maybe<T> Empty()
  26.     {
  27.         return new Maybe<T>();
  28.     }
  29. }

Un punto importante es que no es una clase, es una estructura. Eso es para evitar que alguien que declare que devuelve un Maybe<T> termine devolviendo un null (recordad que queremos evitar los null).

Vale, esta estrcutura, tal cual está no nos aporta casi nada útil. El método del Modelo que presentaba Juan quedaría ahora como:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     Persona persona = new Persona();
  4.     if (nombre == "pepe")
  5.     {
  6.         persona = new Persona { Nombre = nombre, Edad = 14 };
  7.         return new Maybe<Persona>(persona);
  8.     }
  9.     return Maybe<Persona>.Empty();
  10. }

O devolvemos un Maybe relleno con la persona o devolvemos un Maybe vacío.  El test que usaba Juan quedaría como sigue:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     Assert.AreEqual(string.Empty, persona.Value.Nombre);
  7. }

Este test falla y la razón es obvia: persona.Value es null por lo que persona.Value.Nombre da un NullReferenceException. Podría añadir un if en el código para validar si person.IsEmpty es true, y en este caso no hacer nada. Personalmente prefiero mil veces un if (person.IsEmpty) que un if (person.Nombre ==””) ya que el primer if deja mucho claro que se pretende. Pero está claro, que no hemos ganado mucho. Como digo, dicha estructura apenas aporta nada.

Lo bueno es preparar dicha estructura para que pueda ser usada como un monad. Lo siento, soy incapaz de encontrar palabras sencillas para definir que es un monad porque el concepto es muy profundo, así que os dejo con el enlace de la wikipedia: http://en.wikipedia.org/wiki/Monad_(functional_programming)

Ahora vamos a preparar nuestra estructura para que pueda ser usada como un monad:

  1. public struct Maybe<T>
  2. {
  3.  
  4.     private readonly T _value;
  5.     private readonly bool _isEmpty;
  6.     private readonly bool _initialized;
  7.  
  8.     public T Value
  9.     {
  10.         get { return _value; }
  11.     }
  12.  
  13.     public bool IsEmpty
  14.     {
  15.         get { return (!_initialized) || _isEmpty; }
  16.     }
  17.  
  18.     public Maybe(T value)
  19.     {
  20.         _value = value;
  21.         _isEmpty = ((object)value) == null;
  22.         _initialized = true;
  23.     }
  24.  
  25.     public static Maybe<T> Empty()
  26.     {
  27.         return new Maybe<T>();
  28.     }
  29.  
  30.     public void Do(Action<T> action)
  31.     {
  32.         if (!IsEmpty) action(Value);
  33.     }
  34.  
  35.     public void Do(Action<T> action, Action elseAction)
  36.     {
  37.         if (IsEmpty)
  38.         {
  39.             action(Value);
  40.         }
  41.         else
  42.         {
  43.             elseAction();
  44.         }
  45.     }
  46.  
  47.     public TR Do<TR>(Func<T, TR> action)
  48.     {
  49.         return Do(action, default(TR));
  50.     }
  51.  
  52.     public TR Do<TR>(Func<T, TR> action, TR defaultValue)
  53.     {
  54.         return IsEmpty ? defaultValue : action(Value);
  55.     }
  56.  
  57.  
  58.     public Maybe<TR> Apply<TR>(Func<T, TR> action)
  59.     {
  60.         return IsEmpty ? Maybe<TR>.Empty() : new Maybe<TR>(action(Value));
  61.     }
  62. }

He añadido dos familias de métodos:

  1. Método Do para hacer algo solo si Maybe tiene valor
  2. Método Apply para encadenar Maybes. Este es el más potente y lo veremos luego.

Empecemos por los métodos Do. Dichos métodos básicamente nos permiten evitar el if(). Son poco más que una pequeña ayuda que nos proporciona la estructura. Mi test quedaría de la siguiente manera:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     var name = string.Empty;
  7.     persona.Do(p => name = p.Nombre);
  8.     Assert.AreEqual(string.Empty, name);
  9. }

El código del Do se ejecuta solo si hay valor, es decir si se ha devuelto una persona.

Podríamos reescribir el test usando otra de las variantes de Do:

  1. [TestMethod]
  2. public void GetPersonaByName_con_null_devuelve_string_empty()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName(null);
  6.     var name = persona.Do(p => p.Nombre, "no_name");
  7.     Assert.AreEqual("no_name", name );
  8. }

No hay mucho más que decir sobre los métodos Do… porque el método más interesante es Apply ;)

El método Apply me permite encadenar Maybes. Para ver su potencial, cambiaré el método del Modelo:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     Persona persona = new Persona();
  4.     if (nombre == "pepe")
  5.     {
  6.         persona = new Persona {Nombre = null, Edad = 42};
  7.         return new Maybe<Persona>(persona);
  8.     }
  9.     return Maybe<Persona>.Empty();
  10. }

Ahora si le paso “pepe” me da a devolver una Persona pero con el Nombre a null. Tratar esos casos con ifs se vuelve muy complejo y costoso. Apply viene en nuestra ayuda:

  1. [TestMethod]
  2. public void Acceder_a_nombre_null_no_da_probleamas()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName("pepe");
  6.     // En este punto tenemos un Maybe relleno pero value.Nombre es null
  7.     var nombreToUpper = string.Empty;
  8.     nombreToUpper = persona.Apply(p => p.Nombre).Do(s =>s.ToUpper(), "NO_NAME");
  9.     Assert.AreEqual("NO_NAME", nombreToUpper);
  10. }

La variable persona es un Maybe<Persona> con un valor. El método Apply lo que hace es básicamente ejecutar una transformación sobre el valor (el objeto Persona) y devolver un Maybe con el resultado. En este caso transformamos el objeto persona a p.Nombre, por lo que el valor devuelto por Apply es un Maybe<string>. Y como el valor de p.Nombre era null, el Maybe está vacío.

La combinación de Apply y Do permite tratar con valores nulos de forma muy sencilla y elegante.

Si os pregunto que capacidades funcionales tiene C# seguro que muchos responderéis LINQ… Porque no hacemos que nuestra clase Maybe<T> pueda participar del juego de LINQ? Por suerte eso es muy sencillo. Para ello basta con que Maybe<T> implemente IEnumerable<T> añadiendo esas dos funciones:

  1. public IEnumerator<T> GetEnumerator()
  2. {
  3.     if (IsEmpty) yield break;
  4.     yield return _value;
  5. }
  6.  
  7. IEnumerator IEnumerable.GetEnumerator()
  8. {
  9.     return GetEnumerator();
  10. }

Básicamente un Maybe<T> lleno se comporta como una colección de un elemento de tipo T, mientras que un Maybe<T> vacío se comporta como una colección vacía. A partir de aquí… tenemos todo el poder de LINQ para realizar transformaciones, consultas, uniones, etc… con nuestros Maybe<T> con otros Maybe<T> o cualquier otra colección. P. ej. podríamos tener el siguiente código:

  1. [TestMethod]
  2. public void Comprobar_Que_Nombre_es_Null()
  3. {
  4.     var modelo = new Modelo();
  5.     var persona = modelo.GetPersonaByName("pepe");
  6.     var tiene_nombre= persona.Apply(p => p.Nombre).Any();
  7.     Assert.IsFalse(tiene_nombre);
  8. }

Y por supuesto podemos iterar con foreach sobre los elementos de un Maybe<T> :)

Ya para finalizar vamos a añadir un poco más de infrastructura a la clase Maybe<T>. En concreto soporte para la comparación:

  1. public static bool operator ==(Maybe<T> one, Maybe<T> two)
  2. {
  3.     if (one.IsEmpty && two.IsEmpty) return true;
  4.     return typeof(T).IsValueType ?
  5.         EqualityComparer<T>.Default.Equals(one._value, two._value) :
  6.     object.ReferenceEquals(one.Value, two.Value);
  7. }
  8.  
  9. public bool Equals(Maybe<T> other)
  10. {
  11.     return _isEmpty.Equals(other._isEmpty) && EqualityComparer<T>.Default.Equals(_value, other._value);
  12. }
  13.  
  14. public override bool Equals(object obj)
  15. {
  16.     if (ReferenceEquals(null, obj)) return false;
  17.     return obj is Maybe<T> && Equals((Maybe<T>)obj);
  18. }
  19.  
  20. public static bool operator !=(Maybe<T> one, Maybe<T> two)
  21. {
  22.     return !(one == two);
  23. }
  24.  
  25. public override int GetHashCode()
  26. {
  27.     unchecked
  28.     {
  29.         return (_isEmpty.GetHashCode() * 397) ^ EqualityComparer<T>.Default.GetHashCode(_value);
  30.     }
  31. }

Maybe<T> intenta replicar el comportamiento de comparación de T. Es decir:

  • Dos Maybe<T> son “Equals” si los dos Ts de cada Maybe son “Equals”
  • Un Maybe<T> == otro Maybe<T> si:
    • Ambos Ts son el mismo objeto (en el caso de tipo por referencia)
    • Ambos Ts son “Equals” en el caso de tipos por valor

P. ej. el siguiente test valida el comportamiento de ==:

  1. var i = 10;
  2. var i2 = 10;
  3. var p = new Persona();
  4. var p2 = new Persona();
  5. Assert.IsTrue(new Maybe<int>(i) == new Maybe<int>(i2));
  6. Assert.IsFalse(new Maybe<Persona>(p) == new Maybe<Persona>(p2));

Y finalmente añadimos soporte para la conversión implícita de Maybe<T> a T:

  1. public static implicit operator Maybe<T>(T from)
  2. {
  3.     return new Maybe<T>(from);
  4. }

Dicha conversión nos permite simplificar las funciones que deben devolver un Maybe<T>. Así ahora la función del modelo puede ser:

  1. public Maybe<Persona> GetPersonaByName(string nombre)
  2. {
  3.     return nombre == "pepe" ?
  4.         new Persona {Nombre = null, Edad = 42} :
  5.         null;
  6. }

Fíjate que la función GetPersonaByName sigue devolviendo un Maybe<Persona> pero para el código es como si devolviese un Persona. El return null se traduce a devolver un Maybe<Persona> vacío.

Bueno… con eso termino el post. Espero que os haya resultado interesante y que hayáis visto otras posibles maneras de lidiar con las dichosas referencias null.

Saludos!

con no comments
Archivado en:

Una de las características de ASP.NET vNext son los “comandos K”, es decir aquellos comandos que se invocan desde línea de comandos a través del fichero K.cmd.

Dichos comandos están definidos en el project.json y la idea es que ofrezcan tareas necesarias durante el ciclo de vida de compilación y pruebas. Así podemos tener un comando (p. ej. K run) que nos ejecute el proyecto y otro (K test) que nos lance los tests unitarios. Recordad siempre que ASP.NET vNext se crea con el objetivo de que sea multiplataforma total: no solo que sea ejecutable en Linux o MacOSX a través de Mono, si no que sea posible desarrollar en esos sistemas operativos. Y eso implica “desligarse” de Visual Studio. Por supuesto, eso no quita que VS añada e implemente su propio soporte para el ciclo de vida de compilación y pruebas.

De hecho, incluso actualmente en VS no es nuevo usar comandos para gestionar algunas de las tareas necesarias para el ciclo de vida. Así muchos de vosotros conoceréis el comando Install-Package para instalar un paquete NuGet. Así, a dia de hoy, usamos la “Package Manager Console” que no es más que un wrapper sobre PowerShell. Para algunos comandos (como el citado Install-Package) el propio VS ofrece una alternativa gráfica pero hay otros comandos que solo se pueden lanzar desde dicha consola. El ejemplo más claro son todos los comandos de EF Migrations (p. ej. Update-Database).

La razón de que ASP.NET vNext se “olvide” de los comandos Powershell es que Powershell solo funciona en Windows y recuerda… ASP.NET vNext es multiplataforma de verdad.

Por supuesto nosotros podemos crear nuestros propios “comandos K” para añadir tareas que nos sean necesarias o útiles para el ciclo de vida de compilación y pruebas de la aplicación. Porque básicamente un comando definido en el project.json lo único que hace es invocar a un ensamblado.

Vamos a ver como podemos hacerlo. :)

Los comandos residen en un assembly que debe ser compilado con vNext. En nuestro caso vamos a crear una “ASP.NET vNext Console Application” usando VS14 CTP3. En mi caso la he llamado TestCommands.

image

La verdad es que el template que viene con VS14 CTP3 para dicho tipo de aplicaciones es casi inútil, pero bueno… menos da una piedra. Dicho template genera la clásica clase “Program” con su método Main, útil para ejecutables, pero en nuestro caso nuestra aplicación estará lanzada a través de un comando K, así que será el propio framework de ASP.NET vNext quien invocará nuestra aplicación.

Lo primero es agregar una dependencia a Microsoft.Framework.Runtime.Common en nuestro project.json:

  1. "dependencies": {
  2.     "Microsoft.Framework.Runtime.Common": "1.0.0-alpha3"
  3. },

Luego podemos modificar la clase Program para que quede de la siguiente así:

  1. public class Program
  2. {
  3.     public Program(IApplicationEnvironment env)
  4.     {
  5.         Console.WriteLine("In Progrm.ctor " + env.ApplicationBasePath);
  6.     }
  7.     public void Main(string[] args)
  8.     {
  9.         Console.WriteLine("In main");
  10.         Console.ReadLine();
  11.     }
  12. }

Si lo ejecutas con VS directamente verás algo parecido:

image

Por supuesto puedes ejecutarlo también usando KRE. Para ello asegúrate de tener en el path la carpeta donde está KRE instalado. Puedes tener varios KREs instalados side by side y por defecto se instalan en %HOME%\.kre\packages. Así p. ej. en mi maquina tengo:

image

Debes agregar al path la carpeta bin del KRE que quieras usar. Así p. ej. yo tengo agregado al path la carpeta C:\Users\etomas\.kre\packages\KRE-svr50-x86.1.0.0-alpha3\bin

Así si navegamos a la carpeta donde está el fichero project.json y ejecutamos “k run”:

image

En el caso de que os de un error de que no puede encontrar algún assembly lanzando un mensaje de error parecido al siguiente:

System.InvalidOperationException: Failed to resolve the following dependencies:
   Microsoft.Framework.Runtime.Common 1.0.0-alpha3

Debes ejecutar el comando “kpm restore”. Eso es necesario cuando la maquina en la que estás ejecutando no tiene alguno de los paquetes marcados como dependencias en el project.json. Si es la propia maquina en la que tienes VS14 eso no te ocurrirá (VS14 se descarga los paquetes) pero, p. ej. yo he copiado el proyecto a otra máquina donde no tenía VS14, pero sí el runtime de vNext y he necesitado ejecutar dicho comando.

Fijate en cuatro detalles:

  1. Se pasa primero por el constructor de la clase Program
  2. El framework nos inyecta automáticamente el objeto IApplicationEnvironment
  3. Luego se llama automáticamente al método Main
  4. No es necesario que el método Main sea estático.

Si te preguntas como pasar parámetros al método main, pues simplemente añadiéndolos al comando “k run”. Así, si p. ej. tecleas “k run remove /s” el método Main recibirá dos parámetros (“remove” y “/s”). Para pasar parámetros con VS debes usar las propiedades del proyecto (sección Debugging).

Podríamos implementar un comando “list” que listase todos los ficheros del proyecto:

  1. public class Program
  2. {
  3.     private readonly string _folder;
  4.     public Program(IApplicationEnvironment env)
  5.     {
  6.         _folder = env.ApplicationBasePath;
  7.     }
  8.     public void Main(string[] args)
  9.     {
  10.         if (args.Length == 1 && args[0] == "list")
  11.         {
  12.             ListFiles();
  13.         }
  14.         Console.ReadLine();
  15.     }
  16.  
  17.     private void ListFiles()
  18.     {
  19.         var files = Directory.EnumerateFiles(_folder, "*.*", SearchOption.AllDirectories);
  20.         foreach (var file in files)
  21.         {
  22.             Console.WriteLine(file);
  23.         }
  24.     }
  25. }

Para poder usar la clase Directory debes añadir una dependencia en el project.json al paquete System.IO.FileSystem (recuerda que en vNext todo el framework está fragmentado y dividido en paquetes NuGet).

Bien, ahora ya sabemos que podemos ejecutar este programa con “k run list”, pero a nosotros lo que nos interesa es tener un “comando k” adicional para usar con otro programa vNext.

Para ver como lo podemos montar vamos a agregar otro proyecto de consola de ASP.NET vNext a la solución. Yo lo he llamado DemoLauncher. No es necesario que toques nada del código.

Ahora debes agregar una dependencia desde DemoLauncher al otro proyecto (que en mi caso se llamaba TestCommands):

  1. "dependencies": {
  2.     "TestCommands": ""
  3. },

Puedes agregar esta dependencia porque, a pesar de que TestCommands no

está en NuGet está en la misma solución.

Ahora damos de alta el comando. Los comandos se dan de alta en el project.json. Así editamos el project.json de DemoLauncher para añadir una sección de commands:

  1. "commands": {
  2.     "tc" : "TestCommands"
  3. }

Con esto le indicamos que cuando se lance el comando “tc” se invoque al ensamblado “TestCommands”.

Ahora puedo ir a la carpeta donde está el project.json de DemoLauncher y si tecleo “k run” se ejecutará DemoLauncher (eso era de esperar):

image

Pero lo bueno viene ahora. Si desde esa misma carpeta tecleas “k tc list” se te listarán todos los ficheros que haya en la carpeta (y subcarpetas) de DemoLauncher:

image

Al lanzar “k tc” como en el project.json hay definido el comando “tc” se invoca al ensamblado “TestCommands” y se le pasan los parámetros que se hayan pasado después de tc. Así pues TestCommands recibe el parámetro “list” y lista todos los ficheros. Lo interesante es que TestCommands se ejecuta bajo el contexto de ejecución de DemoLauncher (la propiedad ApplicationBasePath del IApplicationEnvironment apunta al directorio donde está DemoLauncher).

En un escenario final real, tendríamos “TestCommands” publicado a NuGet, pero el resto vendría a ser lo mismo que hemos visto.

La gente de EF ya ha empezado a usar esa táctica para los comandos de Migrations (y así posibilitar el uso de Migrations en entornos no windows al no depender más de Powershell). Y personalmente creo que vamos a ver bastantes de esos futuros comandos.

Un saludo!

con 1 comment(s)
Archivado en:

En ASP.NET vNext se unifican MVC y WebApi en una nueva API llamada MVC6. Aunque MVC6 se parece a MVC5 no es compatible con ella, del mismo modo que WebApi se parece a MVC pero por debajo son muy distintas.

Ya hemos viso algunas de las novedades o cambios que trae MVC6 (temas de model binding, controladores POCO, …) y en este post vamos a explorar uno más: los ViewComponents.

Resumiendo: los ViewComponents sustituyen a las vistas parciales. Ya no existe este concepto en MVC6. De hecho, tampoco nos engañemos, desde razor la diferencia entre vistas parciales y vistas normales (a nivel del archivo .cshtml) es muy pequeña: se puede usar una vista parcial como vista normal tan solo cambiando el “return PartialView()” por un “return View()” (o viceversa). En el motor de vistas de ASPX eso no era así, ya que las vistas eran archivos .aspx y las vistas parciales eran archivos .ascx.

En Razor la única diferencia actual entre una vista parcial y una normal es que en la segunda se procesa el archivo de Layout (usualmente _Layout.cshtml) y en la primera no. Pero no es el archivo .cshtml quien determina si es vista normal o parcial. Es el ActionResult devuelto. Si devuelves un ViewResult el archivo .cshtml se procesará como vista normal. Si devuelves un PartialViewResult el archivo .cshtml se procesará como vista parcial.

El código “clásico” en MVC5 para tener una vista parcial era algo como:

  1. public ActionResult Child()
  2. {
  3.     return PartialView();
  4. }

El problema con este enfoque es que esta acción es enrutable, por lo que cualquiera puede ir a la URL que enrute esa acción (p. ej. Home/Child si suponemos HomeController) y recibirá el contenido HTML de la vista parcial. Eso, generalmente, no se desea (para algo la vista es parcial).

Para solventar esto, en MVC4 se añadió el atributo [ChildActionOnly] que evitaba que una acción se enrutase. Así si decoramos la acción con dicho atributo cuando el usuario navega a la URL que debería enrutar dicha acción recibirá un error:

image

La acción se puede invocar a través del helper Html.RenderAction:

  1. @{ Html.RenderAction("Child", "Home"); }

Nota: Se puede usar Html.Partial o Html.RenderPartial para renderizar una vista parcial directamente (sin pasar por un controlador). Eso es útil en el caso de que no haya lógica asociada a dicha vista parcial (si la hay, lo suyo es colocarla en la acción y usar Html.RenderAction).

Bueno… así tenemos las cosas hoy en día: básicamente colocamos las acciones “hijas” en un controlador (porque es donde podemos colocar lógica) pero luego las quitamos del sistema de enrutamiento (con [ChildActionOnly]) y las llamamos indicando directamente que acción y que controlador es.

Realmente ¿tiene sentido que las acciones hijas estén en un controlador? No. Porque la responsabilidad del controlador es, básicamente, responder a peticiones del navegador y eso no es una petición del navegador.

Así en MVC6 se elimina el concepto de vista parcial y el PartialViewResult, y se sustituye por el concepto de ViewComponent. Ahora lo que antes eran acciones hijas son clases propias que derivan de ViewComponent:

  1. [ViewComponent(Name = "Child")]
  2. public class ChildComponent : ViewComponent
  3. {
  4.     public async Task<IViewComponentResult> InvokeAsync()
  5.     {
  6.         return View();
  7.     }
  8. }

El atributo [ViewComponent] nos permite especificar el nombre que damos al componente. El siguiente paso es definir el método InvokeAsync que devuelve una Task<IViewComponentResult> con el resultado. La clase ViewComponent nos define el método View() que devuelve la vista asociada a dicho componente (de forma análoga al método View() de un controlador).

La ubicación por defecto de la vista asociada a un componente es /Views/Shared/Components/[NombreComponente]/Default.cshtml. Es decir en mi caso tengo el fichero Default.cshtml en /Views/Shared/Components/Child:

image

Por supuesto ahora tengo un sitio donde colocar la lógica (si la hubiera) de dicho componente: la propia clase ChildComponent.

Finalmente nos queda ver como renderizamos el componente. Ya no tenemos Html.RenderAction, si no que en su lugar usamos la propiedad Component que tienen las vistas de MVC6:

  1. @await Component.InvokeAsync("Child")

Simplemente le pasamos el nombre del componente (el mismo definido en el atributo [ViewComponent].

Y listos :)

Si desarrollas aplicaciones web con alta carga de código en cliente, es posible que tengas que desarrollarte tus propias funciones JavaScript. Incluso tu propia librería o framework si es el caso.

Una opción es abrir tu editor de texto favorito, crear un archivo .js y empezar a teclear. Total, jQuery viene en un solo archivo .js, ¿verdad? Antes de hacer esto, párate a pensar un poco: ¿a qué nunca colocarías todo el código c# de tu proyecto en un solo fichero .cs? Tenerlo en varios ficheros permite localizar el código más rápidamente, tenerlo mejor organizado y evitar conflictos cuando se trabaja en equipo. Pues bien, eso mismo aplica a JavaScript.

Por supuesto los desarrolladores de jQuery (y de cualquier otra librería decente) no trabajan en un solo fichero. Tienen su código distribuido en ficheros separados que se combinan al final para crear la librería. Vamos a ver como podemos hacer esto de forma sencilla.

Lo primero que tenemos que hacer es precisamente definir los módulos de nuestra librería. Básicamente un módulo es un fichero .js, que define un conjunto de funciones que son accesibles a todos los clientes que usen el módulo. El código de dichos ficheros se organiza siguiendo un patrón que se conoce precisamente como patrón de módulo (revealing module pattern o alguna de sus variantes).

El problema está que en JavaScript no hay (de momento) ninguna manera de establecer las dependencias entre módulos: es decir, de indicar que el módulo A, depende del módulo B (lo que quiere decir que el módulo B debe estar cargad antes que el módulo A). Es ahí donde entra una librería llamada requirejs. Dicha librería permite especificar las dependencias entre módulos. Veamos un ejemplo de módulo con soporte para require:

  1. define([], function () {
  2.     var rnd = function () {
  3.         return 42;
  4.     };
  5.  
  6.     var isOdd = function (i) {
  7.         return (i % 2) != 0;
  8.     };
  9.  
  10.     return {
  11.         rnd: rnd,
  12.         isOdd: isOdd
  13.     };
  14.  
  15. });

Lo importante ahí es la funcion define. Dicha función está definida en requirejs y suele tomar dos parámetros:

  1. Un array con las dependencias del módulo.
  2. Una función con el código del módulo. Dicha función puede recibir como parámetro los módulos especificados en las dependencias.

El módulo define dos funciones (rnd y isOdd) y luego las exporta. Exportarlas significa que las hace visibles al resto de módulos que dependan de este. La forma de exportar es devolviendo un resultado: todo lo que se devuelve, es exportado.

Ahora vamos a declarar otro módulo (llamémosle main.js) que hará uso de este módulo que hemos definido:

  1. define(['math/rnd'], function (math) {
  2.     var addOnlyIfOdd = function (a, b) {
  3.         var result = 0;
  4.         if (math.isOdd(a)) result += a;
  5.         if (math.isOdd(b)) result += b
  6.         return result;
  7.     };
  8.  
  9.     return {
  10.         addOnlyIfOdd: addOnlyIfOdd
  11.     };
  12. });

Es lo mismo que antes con la diferencia de que ahora el array de dependencias contiene el módulo (math/rnd). Este es nuestro módulo anterior. El nombre de un módulo es el nombre del fichero js tal cual (incluyendo la ruta) (existen los llamados módulos AMD donde esto no tiene porque ser así, pero tampoco es relevante ahora). Por lo tanto, en este código, el módulo anterior lo habíamos guardado en math/rnd.js Dentro de este módulo podemos usar todo lo que el módulo anterior exportaba a través del parámetro math.

¿Como usaríamos eso desde una página web?

Lo primero es cargar requirejs desde la página web y usar data-main para indicarle a require el módulo “inicial”:

  1. <!DOCTYPE html>
  2. <head>
  3.     <title>Demo</title>
  4.     <script data-main="./main.js" src="http://requirejs.org/docs/release/2.1.14/minified/require.js"></script>
  5. </head>
  6. <body>
  7. </body>

Si ejecutamos este fichero html y miramos la pestaña Network veremos como no solo se carga el fichero requirejs si no que tanto main.js como también math/rnd.js se cargan: require ha cargado tanto el módulo inicial como todas sus dependencias.

Esto nos permite dividir nuestro código JavaScript con la tranquilidad de que luego los scripts se cargarán en el orden correcto, gracias a requirejs.

Vale, pero estarás diciendo que jQuery viene en un solo js único y para cargar jQuery no debes usar require para nada. Y tienes razón. La realidad es que la gente de jQuery (y tantos otros frameworks) tienen su código dividido en módulos pero luego los unen todos en el orden correcto en un solo js. Y eso lo hacen cuando “construyen” jQuery. Vamos a ver un ejemplo usando gulp (en jQuery se usa grunt que es similar).

Gulp es un sistema de build basado en JavaScript. Se basa en node (así que es necesario tener node instalado). Pero teniendo node instalado, instalar gulp es seguir dos pasos:

  1. Ejecutar npm install –g gulp. Eso instala gulp a nivel “global”. Debe ejecutarse una sola vez.
  2. Ejecutar npm install --save-dev gulp. Eso debe ejecutarse desde el directorio donde tenemos el código fuente de nuestra librería. Eso instala gulp a nivel local y debe hacerse una vez por cada librería instalada.

El tercer paso es crear un fichero javascript, llamado gulpfile.js que será el que tenga las distintas tareas para generar nuestro proyecto JavaScript.

Para el ejemplo vamos a suponer que tenemos nuestro fichero main.js y el fichero math/rnd.js dentro de una subcarpeta src. El fichero gulpfile.js está en la carpeta que contiene a /src. Es decir tenemos una estructura tal que así:

image

Ahora vamos a crear una tarea en gulp para combinar todos los módulos de nuestro proyecto en un .js final. Para ello tenemos que instalar requirejs como módulo de node mediante npm install --save-dev requirejs (desde el directorio de nuestro proyecto).

Finalmente podemos crear una tarea, llamada “make” en nuestro gulpfile:

  1. var gulp = require('gulp');
  2. var requirejs = require('requirejs');
  3. gulp.task('make', function () {
  4.     var config = {
  5.         baseUrl: 'src',
  6.         name: 'main',
  7.         out: 'dist/demo.js',
  8.         findNestedDependencies: true,
  9.         skipSemiColonInsertion: true,
  10.         optimize: 'none'
  11.     };
  12.     requirejs.optimize(config, function (buildResponse) {
  13.         console.log(buildResponse);
  14.     });
  15. });

Cargamos los módulos de node que vamos a usar (gulp y require) y luego usamos requirejs.optimize para cargar el módulo incial, junto con todas sus dependencias y generar un solo .js. Esto es precisamente lo que hace la llamada a requirejs.optimize. El primer parámetro es la configuración. Existen muchas opciones de configuración, siendo las más importantes:

  1. baseUrl: Directorio base donde están los módulos
  2. name: Módulo inicial
  3. out: Fichero de salida
  4. optimize: Si debe minimificar el archivo de salida o no.

Una vez hemos modificado el fichero gulpfile.js ya podemos ejecutarlo, mediante gulp make. Esto invocará la tarea ‘make’ del fichero gulpfile:

image

Una vez ejecutado en el directorio dist tendremos un fichero (demo.js) con todos nuestros módulos combinados en el orden correcto. Este sería el fichero que distribuiríamos y que se usaría desde el navegador.

Eso nos permite tener nuestro código javascript bien separado y organizado, con la tranquilidad de que siempre podemos generar la versión unificada “final” usando gulp.

Por supuesto gulp sirve para mucho más que combinar los módulos. Puede minimificar salidas, pasar tests unitarios, realizar optimizaciones adicionales, tareas propias… en fin, cualquier cosa que uno esperaría de un sistema de builds.

¡Un saludo!

con no comments
Archivado en:

Bien, en el post anterior comentamos cuatro cosillas sobre el model binding en ASP.NET MVC y WebApi, sus semejanzas y sus diferencias. En ASP.NET vNext ambos frameworks se unifican así que es de esperar que el model binding también lo haga… Veamos como funciona el model binding de vNext.

Nota: Este post está realizado con la versión de ASP.NET vNext que viene con el VS14 CTP2. La mejor manera de probar dicha CTP es usando una VM en Azure creada a partir de una plantilla que ya la contiene instalada. Por supuesto todo lo dicho aquí puede contener cambios en la versión final :)

Pruebas de caja negra

Antes que nada he intentado hacer unas pruebas de “caja negra” para ver si el comportamiento era más parecido al de WebApi o al de MVC. He empezado con un proyecto web vNext vacío, y en el project.json he agregado la referencia a Microsoft.AspNet.Mvc. Luego me he creado un controlador como el siguiente:

  1. public class HomeController : Controller
  2. {
  3.     public IActionResult Index(Product product, Customer customer)
  4.     {
  5.         return View();
  6.     }
  7.  
  8.     public bool Post(Product product, Customer customer)
  9.     {
  10.         return true;
  11.     }
  12. }

Finalmente en el Startup.cs he configurado una tabla de rutas que combine MVC y WebApi:

  1. public class Startup
  2. {
  3.     public void Configure(IBuilder app)
  4.     {
  5.         app.UseServices(s => s.AddMvc());
  6.  
  7.         app.UseMvc(r =>
  8.         {
  9.             r.MapRoute(
  10.                 name: "default",
  11.                 template: "{controller}/{action}/{id?}",
  12.                 defaults: new { controller = "Home", action = "Index" });
  13.             r.MapRoute(
  14.                 name: "second",
  15.                 template: "api/{Controller}/{id?}"
  16.                 );
  17.         });
  18.     }
  19. }

Con esa tabla de rutas un POST a /Home/Index debe enrutarme por la primera acción del controlador (al igual que un GET). Mientras que un POST a /api/Home debe enrutarme por la segunda acción del controlador (mientras que un GET a /api/Home debe devolverme un 404). Para más información echa un vistazo a mi post sobre el routing en vNext.

Las clases Customer y Product contienen simplemente propiedades:

  1. public class Customer
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5.     public string Gender { get; set; }
  6. }
  1. public class Product
  2. {
  3.     public int Id { get; set; }
  4.     public string Name { get; set; }
  5. }

Luego he usado cURL para realizar unos posts y ver que es lo que tenía:

curl --data "Id=1&Name=eiximenis&Gender=Male" http://localhost:49228/  --header "Content-type:application/x-www-form-urlencoded"

Con esto simulo un post a que contenga los datos Id, Name y Gender y eso es lo que recibo en el controlador (en el método Index):

image

Este comportamiento es el mismo que en ASP.NET MVC. Ahora cambio la petición de cURL para enviar la misma petición pero a /api/Home para que se me enrute al método Post (estilo WebApi). Mi idea era ver si para enrutamiento tipo MVC se usaba un binding parecido a MVC y para enrutamiento tipo WebApi (sin acción y basado en verbo HTTP) se usaba un binding parecido al de WebApi:

curl –data "Id=1&Name=eiximenis&Gender=Male" http://localhost:49228/api/Home  --header "Content-type:application/x-www-form-urlencoded"

El resultado es que se me llama al método Post del controlador pero recibo exactamente los mismos valores que antes. Recordad que en WebApi eso NO era así. Así a simple vista parece que se ha elegido el modelo de model binding de ASP.NET MVC antes que el de web api.

Otra prueba ha sido realizar un POST contra /api/Home/10 (el parámetro 10 se corresponde al route value id) y dado que estamos pasando el id por URL quitarlo del cuerpo de la petición:

curl --data "Name=eiximenis&Gender=Male" http://localhost:49228/api/Home/10  --header "Content-type:application/x-www-form-urlencoded"

El resultado es el mismo que en el caso anterior (y coincide con ASP.NET MVC donde el model binder ni se preocupa de donde vienen los datos).

Por lo tanto estas pruebas parecen sugerir que en vNext el model binding que se sigue es el de ASP.NET MVC.

Claro que cuando uno pruebas de caja negra debe tener presente el máximo número de opciones… Porque resulta que si hago algo parecido a:

curl –data "{'Name':'eiximenis','Gender':'Male'}"http://localhost:49228/api/Home  --header "Content-type:application/json"

Entonces resulta que ambos parámetros son null. Parece ser que vNext no enlaza por defecto datos en JSON, solo en www-form-urlencoded. Además mandar datos en JSON hace que los parámetros no se enlacen. Aunque mande datos a través de la URL (p. ej. como route values) esos no se usan.

Por supuesto vNext soporta JSON, pero es que nos falta probar una cosilla…

Atributo [FromBody]

De momento en vNext existe el atributo [FromBody] (pero no existe el [FromUri]). Ni corto ni perezoso he aplicado el FromBody a uno de los parámetros del controlador:

  1. public bool Post(Product product, [FromBody] Customer customer)
  2. {
  3.     return true;
  4. }

Y he repetido la última petición (el POST a /api/Home/10). Y el resultado ha sido… un error:

System.InvalidOperationException: 415: Unsupported content type Microsoft.AspNet.Mvc.ModelBinding.ContentTypeHeaderValue

He modificado la petición cURL para usar JSON en lugar de form-urlencoded:

curl --data "{'Name':'eiximenis','Gender':'Male'}" http://localhost:49228/api/Home/10  --header "Content-type:application/json"

Y el resultado ha sido muy interesante:

image

El parámetro customer se ha enlazado a partir de los datos en JSON del cuerpo (el Id está a 0 porque es un route value y no está en el cuerpo de la petición) pero el parámetro product está a null. Por lo tanto el uso de [FromBody] modifica el model binding a un modelo más parecido al de WebApi.

WebApi solo permite un solo parámetro enlazado desde el cuerpo de la petición. Mi duda ahora era si vNext tiene la misma restricción. Mirando el código fuente de la clase JsonInputFormatter intuía que sí… y efectivamente. Aunque a diferencia de WebApi no da error si no que tan solo enlaza el primer parámetro. Así si tengo el método:

  1. public bool Post([FromBody] Product product, [FromBody] Customer customer)

Y repito la llamada cURL anterior, los datos recibidos son:

image

El parámetro product (el primero) se ha enlazado a partir del cuerpo de la petición y el segundo vale null.

¿Y como funciona todo (más o menos)?

Recordad que ASP.NET vNext es open source y que nos podemos bajar libremente el código de su repositorio de GitHub. Con este vistazo al código he visto algunas cosillas.

El método interesante es el método GetActionArguments de la clase ReflectedActionInvoker. Dicho método es el encargado de obtener los argumentos de la acción (por tanto de todo el proceso de model binding). Dicho método hace lo siguiente:

  • Obtiene el BindingContext. El BindingContext es un objeto que tiene varias propiedades, entre ellas 3 que nos interesan:
    1. El InputFormatterProvider a usar
    2. El ModelBinder a usar
    3. Los Value providers a usar
  • Obtiene los parámetros de la acción. Cada parametro viene representado por un objeto ParameterDescriptor. Si el controlador acepta dos parámtetros (customer y product) existen dos objetos ParameterDescriptor, uno representando a cada parámetro de la acción. Dicha clase tiene una propiedad llamada BodyParameterInfo. Si el valor de dicha propiedad es null se usa un binding más tipo MVC (basado en value providers y model binders). Si el valor no es null se usa un binding más tipo WebApi (basado en InputFormatters).

Por defecto vNext viene con los siguientes Value Providers:

  1. Uno para query string (se crea siempre)
  2. Uno para form data (se crea solo si el content type es application/x-www-form-urlencoded
  3. Otro para route values (se crea siempre)

La clave está en el uso del atributo [FromBody] cuando tenemos un parámetro enlazado mediante este atributo entonces no se usan los value providers si no los InputFormatters. Pueden haber dado de alta varios InputFormatters pero solo se aplicará uno (basado en el content-type). Por defecto vNext incluye un solo InputFormatter para application/json.

Ahora bien… qué pasa si tengo un controlador como el siguiente:

  1. public IActionResult Index([FromBody] Customer customer, Product product)
  2. {
  3.     return View();
  4. }

Y hago la siguiente petición?

C:\Users\etomas\Desktop\curl>curl –data "{'Name':'eiximenis','Gender':'Male'}" http://localhost:38820/Home/Index/100?Name=pepe --header "Content-type:application/json"

Pues el valor de los parámetros será como sigue:

image

Se puede ver como el parámetro enlazado con el [FromBody] se enlaza con los parámetros del cuerpo (en JSON) mientras que el parámetro enlazado sin [FromBody] se enlaza con el resto de parámetros (de la URL, routevalues y querystring). En vNext el [FromUri] no es necesario: si hay un [FromBody] el resto de elementos deben ser enlazados desde la URL. Si no hay [FromBody] los elementos serán enlazados desde cualquier parte de la request.

Bueno… en este post hemos visto un poco el funcionamiento de ASP.NET vNext en cuanto a model binding. El resumen es que estamos ante un modelo mixto del de ASP.NET MVC y WebApi.

En futuros posts veremos como podemos añadir InputFormatters y ValueProviders para configurar el sistema de model binding de vNext.

Saludos!

Una de las confusiones más habituales con la gente que viene de MVC y pasa a WebApi es que el funcionamiento del model binding (es decir rellenar los parámetros de los controladores a partir de los datos de la request) es distinto entre ambos frameworks. La confusión viene porque a primera vista todo parece que funcione igual pero realmente hay diferencias muy profundas entre ambos frameworks.

Veamos, pues, algunas de las diferencias que hay entre ambos frameworks

ASP.NET MVC Value Providers primero y Model Binders después

En ASP.NET MVC el proceso de model binding está compuesto de dos pasos:

  1. Primero existe una serie de clases, llamadas value providers que se encargan de leer la petición HTTP y colocar los datos en un sitio común (como un diccionario). Así no importa si un dato (p. ej. Id) está en querystring o en formdata). Simplemente, se obtendrá ese Id y se colocará en el ese sitio común. Hay varios value providers porque cada value provider puede analizar una parte de la petición. Así hay un value provider que analiza la querystring, un par que analizan formdata (uno si el content-type es application/x-www-form-urlencoded y otro si es application/json) y así sucesivamente.
  2. Una vez los datos de la petición están en este lugar común entra en acción el model binder. El model binder recoge los datos de ese sitio común y los utiliza para crear los objetos que son pasados como parámetros en la acción del controlador. Cada parámetro es enlazado por un model binder distinto dependiendo del tipo del parámetro (aunque por defecto casi todos los tipos se enlazan usando el DefaultModelBinder, nosotros podemos crear model binders propios y vincularlos a tipos de parámetros).

Supongamos dos clases como las siguientes:

  1. public class Customer
  2. {
  3.    public int Id { get; set; }
  4.    public string Name {  get; set; }
  5.    public string Address{ get; set; }
  6. }
  7.  
  8. public class Product
  9. {
  10.    public int Id { get; set; }
  11.    public string Name { get; set; }
  12. }

Y una vista que envíe los datos:

  1. <form>
  2.     <input type="text" name="Id" /><br />
  3.     <input type="text" name="Name" /><br />
  4.     <input type="text" name="Address" /><br />
  5.     <input type="submit" value="send"/>
  6. </form>

Ahora si tenemos un controlador que tiene una acción con dos parámetros (Customer y Product) ¿qué es lo que recibimos? Pues lo siguiente:

image

Es decir el valor de las propiedades Id y Name se ha enlazado a ambos parámetros. Es lógico: los value providers han recogido 3 valores de la petición resultado de enviar el formulario (Id, Name y Address) y los han colocado en el “sitio común”. Luego cuando el model binder del parámetro customer debe crear el objeto usa los valores de dicho sitio común para enlazar las propiedades… lo mismo que el model binder del parámetro product. De ahí que las propiedades que se llamen igual tengan el mismo valor.

Por supuesto esta posibilidad está contemplada en ASP.NET MVC, de forma que el model binder entiende de “prefijos”. Así si modifico uno de los campos de texto para que sea:

  1. <input type="text" name="customer.Name" />

Ahora el valor del campo de la petición llamado “customer.Name” se enlazará solo a la propiedad Name del parámetro customer (product.Name será null).

El modelo de value providers + model binders es muy versátil y potente.

WebApi – Básicamente MediaTypeFormatters (básicamente)

En WebApi la aproximación es radicalmente distinta. Primero hay una distinción clara, clarísima, sobre si el parámetro de la acción se enlaza desde el cuerpo de la petición o desde cualquier otro sitio (p. ej. querystring).

En MVC el model binder no sabe de donde vienen los datos que usa para enlazar los parámetros del controlador, ya que los saca siempre del “sitio común” (donde lo dejan los value providers). WebApi usa una orientación distinta pero la regla de oro fundamental es: El cuerpo de la petición puede ser leído una sola vez.

En efecto en WebApi el cuerpo de la petición HTTP es un stream forward-only, lo que significa que puede ser leído una única vez (en ASP.NET MVC el cuerpo está cacheado ya que distintos value providers pueden leerlo). Eso está hecho por temas de rendimiento.

Así en WebApi se usa un paradigma basado básicamente en el content-type. Aparecen unos entes nuevos (los media type formatters) encargados de leer el cuerpo de la petición. Pero SOLO UN media type formatter puede leer el cuerpo (recuerda: puede ser leído una sola vez). Y como sabe WebApi qué media type formatter procesa el cuerpo de la petición? Pues basándose en el content-type.

El media type formatter lee pues el cuerpo de la petición y rellena un objeto de .NET (del tipo indicado) en base a dichos datos. Efectivamente: el media type formatter lee de la petición y enlaza las propiedades. Eso implica una restricción importante: tan solo un parámetro de la acción puede ser obtenido a partir de los datos del cuerpo de una petición.

Veamos la diferencia: he creado un controlador WebApi con un método análogo al anterior que recibe (via POST) un Customer y un Product:

  1. public class DataController : ApiController
  2. {
  3.     public bool Post(Product product, Customer customer)
  4.     {
  5.         return true;
  6.     }
  7. }

Y he modificado la vista para que haga post del formulario a dicho controlador. Existe un media type formatter que se encarga de leer los datos cuando el content-type es application/x-www-form-urlencoded (de hecho, realmente hay dos pero tampoco es necesario entrar en más detalles). ¿Y cual es el resultado? Pues un error. Concretamente una System.InvalidOperationException con el mensaje: Can't bind multiple parameters ('product' and 'customer') to the request's content.

La razón es que tenemos dos parámetros a rellenar basándonos en el cuerpo de la petición. Pero dicho cuerpo puede ser leído una sola vez por un media type formatter. Y un media type formatter tan solo puede enlazar un objeto.

Es ahí donde entra el atributo FromUri. Dicho atributo se aplica a parámetros de la accion para indicar que esos parámetros se deben enlazar a partir de datos encontrados en la URL (querystring):

  1. public bool Post(Product product, [FromUri] Customer customer)
  2. {
  3.     return true;
  4. }

Si ahora ejecutamos de nuevo podemos ver que recibe el controlador son:

image

Podemos ver como customer no tiene datos (a pesar de haber un campo Address en el cuerpo de la petición) porque customer se enlaza a partir de los datos de la URL (querystring).

Por defecto WebApi usa siempre un media type formatter cuando el parámetro es un tipo complejo (una clase) a no ser que haya el atributo FromUri aplicado. Si el parámetro es un tipo simple (p. ej. int o un string) intenta enlazarlo a partir de la URL a no ser que haya aplicado el atributo contrario [FromBody]. Eso sí recuerda que solo un parámetro puede ser enlazado desde el cuerpo de la petición.

WebApi – ModelBinders y Value Providers

Cuando se enlaza un parámetro que no viene del cuerpo de la petición (es decir que viene de la URL) WebApi usa entonces el mismo modelo que MVC. Es decir primero los value providers recogen los datos de la petición (excepto el cuerpo) y los colocan en un sitio común y luego los model binders usan los datos de este sitio cómún para enlazar los parámetros).

P. ej. modifico el controlador de WebApi para que acepte GET y enlace ambos parámetros desde la URL:

  1. public bool Get([FromUri]Product product, [FromUri] Customer customer)
  2. {
  3.     return true;
  4. }

Ahora si realizo una llamada GET a /api/Data/10?name=test lo que obtengo es:

image

Los value providers recogen los datos de la URL (hay dos, uno para los route values (como /10) y otro para la querystring) y los dejan en el sitio común. Luego los model binders usan esos datos para enlazar tanto product como customer y es por eso que obtengo los datos duplicados (es el caso análogo al caso inicial de ASP.NET MVC).

En resumen: hemos visto cuatro pinceladas de como ASP.NET MVC y WebApi enlazan los parámetros a los controladores haciendo especial énfasis en las diferencias. Dejamos para un post posterior el ver como funciona este tema en vNext pues recuerda que en vNext WebApi y MVC son un solo framework :)

Saludos!

con 5 comment(s)
Archivado en: ,

Muy buenas! El otro día recibí el siguiente correo (a través del formulario de contacto del blog):

Estoy desarrollando una serie de web apis para transacciones con tarjetas de crédito. Mi problema es que no encuentro la forma de cifrar los datos sensibles como el numero de tarjeta de crédito. con un web service esta claro como hacerlos pero no encuentro la forma en una web api. que me recomendas?

Es un tema interesante y que da para mucho pero a ver si podemos dar cuatro pinceladas

Antes de nada lo dicho en este post trata de evitar que alguien que use un sniffer para analizar el tráfico de red pueda ver los datos confidenciales que enviamos. Este post no pretende ni puede ser una solución completa al problema, ya que hay muchas casuísticas que se deben tratar y muchos tipos de ataque a los que debemos responder. Honestamente la solución más sencilla pasa por usar HTTPS. Si usas HTTPS delegas toda la seguridad en el canal. Sin duda es lo más sencillo. No hay que hacer nada especial para poder usar HTTPS en WebApi. Si usas IIS Express para desarrollar (lo habitual con VS) habilitar el soporte para HTTPS está a un click de distancia:

SNAGHTML5d216779

Al seleccionar SSL Enabled VS te indica tanto la URL http como la https (con otro puerto claro). Para que todo funcione IIS Express genera un certificado SSL de desarrollo y ya tienes HTTPS habilitado. Por supuesto cuando despliegues en producción deberás desplegar el certificado SSL de producción en IIS.

Conceptos básicos de cifrado

Si no quieres usar IIS entonces debes encriptar los datos en el cliente y desencriptarlos en el servidor. Eso indica que debes especificar que método de encriptación se usa… hay muchos pero a grandes rasgos se dividen en dos grandes grupos:

  1. De clave simétrica
  2. De clave asimétrica

En los métodos de clave simétrica, cliente y servidor comparten una clave. Dicha clave es usada para generar texto cifrado a partir de texto plano… y viceversa. Es decir alguien que conozca la clave puede tanto enviar mensajes cifrados y descifrarlos.

En los métodos de clave asimétrica cada uno de los actores tiene un par de claves. Ambas claves sirven tanto para cifrar como para descifrar, pero con una particularidad: lo cifrado con una clave debe ser descifrado con la otra y viceversa.

Los métodos asimétricos tienen a priori una seguridad mayor que el método simétrico. Imagina a alguien llamado Alice que quiera enviar un mensaje a Bob. Con un método simétrico:

  1. Alice cifraría el mensaje con la clave K de encriptación
  2. Bob usaría la misma clave K para descifrarlo
  3. Si Bob quiere enviar una respuesta cifrada a Alice la cifraría usando la misma clave K y Alice lo descifraría usando K.

El punto débil aquí es el intercambio de claves. Si queremos que un mensaje de Alice a Bob solo pueda ser leído por Bob debemos asegurarnos que la clave K solo sea conocida por Alice y por Bob. Porque si dicha clave K llega a un tercero este podrá leer el mensaje.

Para evitar este punto entran los sistemas asimétricos:

  1. Bob tiene su par de claves Kpu y Kpr. Bob publica la clave Kpu en su web pero se guarda bajo llave la clave Kpr.
  2. Alice quiere mandarle un mensaje cifrado a Bob. Para ello lo cifra con la clave Kpu de Bob (que está en su web).
  3. Bob recibe el mensaje y lo descifra usando su propia clave Kpr (que tiene guardada bajo llave).
  4. Si Bob quiere responder a Alice puede cifrar su respuesta usando la clave Kpu de Alice (que también está en la web de Alice). Alice puede descifrar el texto usando su propia clave Kpr.

No hay intercambio de claves. Par enviar algo a Bob se cifra con su clave pública (la Kpu de Bob) y tan solo Bob lo puede descifrar con su Kpr (su clave privada). Los métodos asimétricos son más lentos (tanto para cifrar como para descifrar) que los asimétricos y tienen sus propios problemas: existen ataques sobre la Kpu para intentar averiguar la Kpr asociada, ya que es obvio que tienen alguna relación. Algunos algoritmos asimétricos han sido rotos gracias a que se han encontrado relaciones más o menos obvias entre ambas claves que permiten deducir (o acotar suficientemente el ámbito de búsqueda para permitir un ataque por fuerza bruta) la clave privada al saber la pública.

Cifrando y descifrando datos

Bueno… veamos como podemos implementar un cifrado asimétrico usando WebApi. Nos centramos solo en el cifrado de datos (no entraremos en temas de firma digital aunque los principios sean muy parecidos). El algoritmo que usaremos será RSA (pero vamos, hay otros) a través de la clase RSACryptoServerProvider.

La propia clase se encarga de crear el par de claves necesario y luego pueden usarse los métodos ToXmlString() y FromXmlString() para guardar dichas claves o bien incorporar dichas claves en un RSACryptoServerProvider:

  1. static void Main(string[] args)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     var both = rsa.ToXmlString(true);
  5.     var pub = rsa.ToXmlString(false);
  6.     File.WriteAllText("both.xml", both);
  7.     File.WriteAllText("pub.xml", pub);
  8. }

Dicho código crea dos archivos xml (both.xml y pub.xml). El primero contiene el par de claves (pública y privada) mientras que el segundo contiene tan solo la clase pública. Por supuesto hay mejores maneras de guardar las claves pero para este post eso será suficiente.

Ahora vamos a crear una aplicación ASP.NET WebApi.

He generado un par de claves y las he guardado en una clase (he copiado el contenido entero de both.xml en una propiedad CryptoKeys.Both y lo mismo para CryptoKeys.Pub):

  1. class CryptoKeys
  2. {
  3.     internal const string Both = "[CONTENIDO ENTERO DEL FICHERO BOTH.XML]";
  4.     internal const string Pub = "[CONTENIDO ENTERO DEL FICHERO PUB.XML]";
  5. }

Vale… en un mundo real esto NO estaría hardcodeado pero… ¡estamos en el mundo ideal de los blogs!

Ahora creamos un controlador WebApi para que nos devuelva la clave pública:

  1. public class KeyController : ApiController
  2. {
  3.     // GET api/values
  4.     public string Get()
  5.     {
  6.         return CryptoKeys.Pub;
  7.     }
  8. }

Una llamada GET a /api/Key nos permite obtener la clave pública del servidor:

SNAGHTML5d500782

Vale… vamos a ver el código del cliente.

  1. static void Main(string[] args)
  2. {
  3.     DoEverythingAsync().Wait();
  4. }
  5.  
  6. private static async Task DoEverythingAsync()
  7. {
  8.     string kpu = null;
  9.     // Obtenemos clave del servidor
  10.     using (var client = new HttpClient())
  11.     {
  12.         client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
  13.         var data = await client.GetAsync("http://localhost:25986/api/key");
  14.         kpu = await data.Content.ReadAsStringAsync();
  15.         kpu = JsonConvert.DeserializeObject<string>(kpu);
  16.     }
  17.  
  18.     var encrypted = EncryptData(kpu, "PRIVATE DATA TO SEND");
  19.     await SendEncryptedDataAsync(encrypted);
  20. }

Básicamente obtenemos la clave pública (llamando a /api/Key del servidor), encriptamos unos datos y los enviamos. El código de EncryptData es:

  1. private static byte[] EncryptData(string kpu, string data)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(kpu);
  5.     var bytes = new byte[data.Length * sizeof(char)];
  6.     Buffer.BlockCopy(data.ToCharArray(), 0, bytes, 0, bytes.Length);
  7.     var encrypted = rsa.Encrypt(bytes, true);
  8.     return encrypted;
  9. }

Simplemente usamos el método FromXmlString para inicializar el RSACryptoServiceProvider con la clave pública obtenida previamente. Luego llamamos a Encrypt para encriptar los datos.

Finalmente el código de SendEncryptedDataAsync:

  1. private static async Task SendEncryptedDataAsync(byte[] encrypted)
  2. {
  3.     using (var client = new HttpClient())
  4.     {
  5.         var content = new FormUrlEncodedContent(new[]
  6.         {
  7.             new KeyValuePair<string, string>("CC", Convert.ToBase64String(encrypted)),
  8.             new KeyValuePair<string, string>("Name", "edu"),
  9.         });
  10.         var result = await client.PostAsync("http://localhost:25986/api/Test", content);
  11.         Console.WriteLine("Status Code: " + result.StatusCode);
  12.     }
  13. }

Enviamos dos campos en el POST, uno llamado CC (el encriptado) y otro llamado Name que NO está encriptado.

Hecho esto, en el servidor nos creamos un controlador de prueba (Test):

  1. public class TestController : ApiController
  2. {
  3.     public void Post(CreditCardInfo card)
  4.     {
  5.         
  6.     }
  7. }
  8.  
  9. public class CreditCardInfo
  10. {
  11.     public string Name { get; set; }
  12.     public string CC { get; set; }
  13. }

Si ahora levantamos la aplicación WebApi y ejecutamos nuestra aplicación de consola cliente vemos que los datos llegan al servidor:

image

Por supuesto el servidor tiene que descifrarlos… Veamos como:

  1. public void Post(CreditCardInfo card)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(CryptoKeys.Both);
  5.     var bytes = Convert.FromBase64String(card.CC);
  6.     var decrypted = rsa.Decrypt(bytes, true);
  7.     var chars = new char[decrypted.Length / sizeof(char)];
  8.     Buffer.BlockCopy(decrypted, 0, chars, 0, decrypted.Length);
  9.     var decryptedString = new string(chars);
  10. }

¡Al final en la variable decryptedString estarán los datos descifrados!

Un poco más de integración con WebApi

Tener que colocar el código de descifrado continuamente no es muy agradecido. Por suerte podemos integrarnos dentro de WebApi de forma relativamente simple.

WebApi usa media type formatters para pasar los datos que vienen en el cuerpo de la petición http a un parámetro del controlador. En nuestro caso mandamos dos datos (Name y CC). Para saber que media type formatter usar WebApi debe saber en qué formato están los datos en la petición y para ello usa la cabecera content-type. El valor normal de content-type cuando enviamos datos vía POST es application/x-www-form-urlencoded (si enviasemos un JSON usaríamos application/json). WebApi viene con soporte nativo para application/x-www-form-urlencoded a través de dos media type formatters:

  • FormUrlEncodedMediaTypeFormatter: Se usa si el controlador recibe un JToken o un FormCollection
  • JQueryMvcFormUrlEncodedFormatter: Se usa si el controlador recibe un objeto como parámetro. Deriva del anterior.

En nuestro caso el controlador recibe un CreditCardInfo por lo que será JQueryMvcFormUrlEncodedFormatter el que procese los datos de la petición, y cree el CreditCardInfo que recibe el controlador en el método POST.

Así la solución pasa por derivar de JQueryMvcFormUrlEncodedFormatter y añadir en la clase derivada el código de descifrado. Veamos como hacerlo de forma sencilla. Primero creamos la clase CypheredFomUrlEncodedMediaTypeFormatter:

  1. public class CypheredFormUrlEncodedMediaTypeFormatter : JQueryMvcFormUrlEncodedFormatter
  2. {
  3.     public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content,
  4.         IFormatterLogger formatterLogger)
  5.     {
  6.         return this.ReadFromStreamAsyncCore(type, readStream, content, formatterLogger);
  7.     }
  8. }

Sobreescribimos el método ReadFromStreamAsync. Este método debe leer los datos del cuerpo de la petición, crear el objeto del tipo type indicado y pasar los datos de la petición al objeto creado.

Todo el código lo tendremos en el método ReadFromStreamAsyncCore de la propia clase:

  1. public async Task<object> ReadFromStreamAsyncCore(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger)
  2.     {
  3.         var obj =
  4.             await base.ReadFromStreamAsync(typeof(FormDataCollection), readStream, content, formatterLogger) as
  5.                 FormDataCollection;
  6.         var binded = obj.ReadAs(type);
  7.         foreach (var property in type.GetProperties().Where(p => p.PropertyType == typeof(string)))
  8.         {
  9.             var attr = property.GetCustomAttribute<CypheredDataAttribute>();
  10.             if (attr != null)
  11.             {
  12.                 property.SetValue(binded, Decrypt(property.GetValue(binded) as string));
  13.             }
  14.         }
  15.  
  16.         return binded;
  17.     }

El método ReadFromStreamAsyncCore hace lo siguiente:

  1. Usamos el método (de la clase base) ReadFromStreamAsync pasándole como parámetro un FormDataCollection. Con eso obtenemos un FormDataCollection (básicamente un diccionario clave, valor) con los datos del cuerpo de la petición. Eso nos funciona porque derivamos de FormUrlEncodedMediaTypeFormatter que tiene soporte para FormDataCollection.
  2. Usamos el método (de extensión) ReadAs de FormDataCollection que crea un objeto del tipo indicado a partir de los datos de un FormDataCollection. Es decir, hace el binding de propiedades.
  3. Ahora iteramos sobre todas las propiedades de tipo cadena y miramos si alguna tiene el atributo CypheredDataAttribute aplicado.
    1. Si lo tiene desciframos el valor de dicha propiedad mediante una llamada a Decrypt.

El código del método Decrypt es básicamente el mismo que teníamos antes en el controlador:

  1. private string Decrypt(string encryptedBase64)
  2. {
  3.     var rsa = new RSACryptoServiceProvider();
  4.     rsa.FromXmlString(CryptoKeys.Both);
  5.     var bytes = Convert.FromBase64String(encryptedBase64);
  6.     var decrypted = rsa.Decrypt(bytes, true);
  7.     var chars = new char[decrypted.Length / sizeof(char)];
  8.     Buffer.BlockCopy(decrypted, 0, chars, 0, decrypted.Length);
  9.     var decryptedString = new string(chars);
  10.     return decryptedString;
  11. }

Con esto ya tenemos un media type formatter que descifrará automáticamente todas aquellas propiedades string decoradas con [CypheredData]. El código de CypheredDataAttribute es trivial:

  1. [AttributeUsage(AttributeTargets.Property)]
  2. public class CypheredDataAttribute : Attribute
  3. {
  4. }

Ahora nuestra clase CreditCardInfo nos queda de la siguiente manera:

  1. public class CreditCardInfo
  2. {
  3.     public string Name { get; set; }
  4.     [CypheredData]
  5.     public string CC { get; set; }
  6. }

¡Un último detalle! Tenemos que agregar nuestro MediaTypeFormatter en la configuración de webapi:

  1. config.Formatters.Remove(
  2.     config.Formatters.Single(f => typeof (JQueryMvcFormUrlEncodedFormatter) == f.GetType()));
  3. config.Formatters.Add(new CypheredFormUrlEncodedMediaTypeFormatter());

Y lo mejor: ¡en el controlador no tenemos que hacer nada para tener los datos descifrados!

image

¡Listos! Hemos visto como podemos enviar datos cifrados y como podemos integrarnos un poco dentro de webapi para que el descifrado sea sencillo. No hemos cubierto todos los casos posibles, pero espero que os haya dado algunas ideas de como implementarlo!

Saludos!

con no comments
Archivado en:

Otra de las cosas que cambia, radicalmente, en ASP.NET vNext es el tema de la configuración. Hasta ahora teníamos, generalmente, mezcladas en un mismo archivo (web.config) tanto la configuración propia de nuestra aplicación (app settings, connection strings y módulos de configuración propios) como la del framework (p. ej. la configuración de forms authentication o de los HttpModules).

En vNext eso cambia radicalmente. Para empezar desaparece web.config. La configuración de los distintos módulos del framework se realiza por código, en el método Configure de la clase Startup. La configuración de nuestra aplicación se mantiene en ficheros de configuración pero desaparecen las nociones de appsettings o cadenas de conexión. Ýa no hay una API más o menos “tipada” como la del ConfigurationManager (donde tenemos propiedades como ConnectionStrings o AppSettings). La configuración de vNext es básicamente un diccionario de claves, valores. Así pues los ficheros de configuración que usemos ya no tienen una estructura predefinida. Desaparece toda noción de esquema en los ficheros de configuración. De hecho se soportan varios formatos: XML, JSON y INI (si, puede ser sorprendente el soporte para INI pero debemos tener presente la aspiración “multiplataforma” de vNext y ficheros INI son muy usados en entornos Unix).

Vamos a ver un ejemplo sencillo del uso del sistema de configuración nuevo de vNext. Para ello partiremos de una aplicación vNext vacía (ASP.NET vNext Empty Web Application).

Una diferencia importante respecto a ASP.NET clásico es que la configuración ahora es un objeto. Ya no hay clases estáticas (como ConfigurationManager). Así que el primer punto es crear dicho objeto en el método Configure de la clase Startup:

  1. public void Configure(IBuilder app)
  2. {
  3.     var config = new Configuration();    
  4. }

El siguiente paso será agregar un archivo json (p. ej. data.json) con el siguiente formato:

  1. {
  2.     "environments": {
  3.         "dev": {
  4.             "background": "blue"
  5.         },
  6.           "pre":{
  7.               "background":"yellow"
  8.         },
  9.           "prod":{
  10.               "background":"red"
  11.         }
  12.     }
  13. }

El “esquema” de los datos JSON es totalmente inventado.

Bien. La clase Configuration tiene un método, llamado Add, al cual debe pasársele un IConfigurationSource. No tiene soporte para cargar distintos tipos de archivo, pero dicho soporte se obtiene a través de métodos de extensión. Uno de ellos es AddJsonConfig que está definido en el paquete Microsoft.Framework.ConfigurationModel.Json, así que debemos agregar una referencia a dicho paquete en project.json (en la sección “dependencies”):

  1. "Microsoft.Framework.ConfigurationModel.Json": "0.1-alpha-build-0233"

Ahora ya podemos usar el método “AddJsonFile”:

  1. config.AddJsonFile("data.json");

Con eso cargamos los valores de dicho fichero de configuración dentro del objeto Configuration. El objeto Configuration es un diccionario plano, pero nuestro JSON tiene profundidad (p. ej. el objeto environments tiene tres propiedades (dev, pre, prod), cada una de las cuales tiene otra propiedad llamada background. Para convertir esa estructura jerárquica a una plana, se añaden las claves usando el carácter dos puntos (:) como separador. Así, después de procesar este JSON el objeto Configuration tendrá tres claves:

  1. environments:dev:background (con el valor blue)
  2. environments:pre:background (con el valor yellow)
  3. environments:prod:background (con el valor red)

Se usa el método Get para acceder a un valor de la configuración. El método Get acepta un solo parámetro: la clave y devuelve el valor (otra cadena). Al ejecutar el siguiente código value valdría “yellow”:

  1. var value = config.Get("environments:pre:background");

Recordad: ¡no hay una sección predeterminada para guardar valores de cadenas de conexión ni appsettings! Nosotros decidimos donde guardar cada cosa que necesitemos.

Vamos a ver ahora como hacer uso de dicha configuración desde un módulo de vNext. P. ej. agreguemos ASP.NET MVC a nuestro proyecto, agregando la referencia a Microsoft.AspNet.Mvc a project.json:

  1. "Microsoft.AspNet.Mvc" :"0.1-alpha-build-1268"

Ahora añadamos el código al método Configure de la clase Startup para inicializar correctamente ASP.NET MVC. Además añadimos el objeto config dentro del sistema de inyección de dependencias de vNext:

  1. public void Configure(IBuilder app)
  2. {
  3.     var config = new Configuration();
  4.     config.AddJsonFile("data.json");
  5.     app.UseServices(
  6.         s =>
  7.         {
  8.             s.AddInstance<IConfiguration>(config);
  9.             s.AddMvc();
  10.         });
  11.     app.UseMvc();
  12. }

Ahora agrega un controlador Home y haz que tenga en su constructor un parámetro de tipo IConfiguration:

  1. public class HomeController : Controller
  2. {
  3.     private readonly string _bg;
  4.  
  5.     public HomeController(IConfiguration cfg)
  6.     {
  7.         _bg = cfg.Get("environments:dev:background");
  8.     }
  9.  
  10.     public IActionResult Index()
  11.     {
  12.         ViewBag.bg = _bg;
  13.         return View();
  14.     }
  15. }

Dado que hemos agregado la configuración dentro del mecanismo de inyección de dependencias de vNext, ahora podemos inyectar dicha configuración por el constructor. Fíjate que en el controlador no nos guardamos toda la configuración si no solo lo que necestiamos. Finalmente si te creas una vista Index.cshtml de prueba (en Views/Home):

  1. <html>
  2. <head><title>Demo Config</title></head>
  3. <body style="background: @ViewBag.bg">
  4.     <h1>Demo config</h1>
  5. </body>
  6. </html>

Ahora al ejecutar la demo deberías ver la página con el fondo azul, pues este es el valor de la entrada environments:dev:background de la configuración:

image

Y eso viene a ser todo… ¡Espero que os haya resultado interesante!

con no comments
Archivado en:

Una de las novedades que presenta ASP.NET MVC6 (integrada dentro de vNext) es la posibilidad de que los controladores ya no deban heredar de ninguna clase base.

De hecho la clase Controller en MVC clásico (MVC5 y anteriores) proporcionaba básicamente dos cosas:

  1. Un conjunto de métodos de para devolver action results (p. ej. el método View() para devolver un ViewResult o el método Json para devolver un JsonResult).
  2. Acceso a algunas propiedades para contexto (ControllerContext, ModelState y ViewBag básicamente).

Los métdos para devolver action results no son estríctamente necesarios (aunque ayudan) pero pueden encapsularse en alguna clase aparte y los objetos de contexto pueden añadirse por inyección de dependencias (ASP.NET vNext está montando desde la base usando inyección de dependencias).

Así en MVC6 podemos crear un controlador como el siguiente:

  1. public class HomeController
  2. {
  3.     // GET: /<controller>/
  4.     public IActionResult Index()
  5.     {
  6.         return null;
  7.     }
  8. }

Si (asumiendo la tabla de rutas tradicional) navegamos hacia /Home/Index veremos como se nos invoca dicho método. Por supuesto ahora hemos de ver como crear el action result necesario. P. ej. supongamos que queremos devolver la vista (lo que sería un return View() en un controlador tradicional). Vemos que el constructor del ViewResult nos pide dos parámetros:

image

Como he dicho antes ASP.NET vNext está montado basado en inyección de dependencias así que… deja que el propio framework te inyecte estos parámetros:

  1. public class HomeController
  2. {
  3.     private readonly IServiceProvider _serviceProvider;
  4.     private readonly IViewEngine _viewEngine;
  5.     public HomeController(IServiceProvider serviceProvider, IViewEngine viewEngine)
  6.     {
  7.         _serviceProvider = serviceProvider;
  8.         _viewEngine = viewEngine;
  9.     }
  10.     public IActionResult Index()
  11.     {
  12.         return new ViewResult(_serviceProvider, _viewEngine);
  13.     }
  14. }

Si ahora ejecutas y colocas un breakpoint en el constructor verás que ambos parámetros han sido inicializados por el framework de ASP.NET vNext:

image

Verás como efectivamente esto devuelve la vista Index.cshtml localizada en Views/Home (exactamente lo mismo que hace return View()).

Pasar un modelo a la vista tampoco es excesivamente complicado:

  1. public class HomeController
  2. {
  3.     private readonly IServiceProvider _serviceProvider;
  4.     private readonly IViewEngine _viewEngine;
  5.     private readonly IModelMetadataProvider _modelMetadataProvider;
  6.     public HomeController(IServiceProvider serviceProvider, IViewEngine viewEngine, IModelMetadataProvider modelMetadataProvider)
  7.     {
  8.         _serviceProvider = serviceProvider;
  9.         _viewEngine = viewEngine;
  10.         _modelMetadataProvider = modelMetadataProvider;
  11.     }
  12.     public IActionResult Index()
  13.     {
  14.         var viewdata = new ViewDataDictionary<FooModel>(_modelMetadataProvider);
  15.         viewdata.Model = new FooModel();
  16.         return new ViewResult(_serviceProvider, _viewEngine) { ViewData = viewdata };
  17.     }

Necesitamos un IModelMetadataProvider (que recibimos también por inyección de dependencias) ya que lo necesitamos para la construcción del ViewDataDictionary que pasamos a la vista.

Para evitar que nuestro controlador POCO deba tomar demasiadas depenencias en el constructor (dependencias que son requeridas básicamente para construir los action results), el equipo de ASP.NET ha creado la interfaz IActionResultHelper. Dicha interfaz contiene métodos para ayudarnos a crear más fácilmente los action results. Por supuesto, en el controlador recibimos un IActionResultHelper por inyección de dependencias. Así podemos modificar nuestro controlador para que quede de la siguiente forma:

  1. public class HomeController
  2. {
  3.     private readonly IActionResultHelper _actionHelper;
  4.     private readonly IModelMetadataProvider _modelMetadataProvider;
  5.     public HomeController(IActionResultHelper actionHelper, IModelMetadataProvider modelMetadataProvider)
  6.     {
  7.         _actionHelper = actionHelper;
  8.         _modelMetadataProvider = modelMetadataProvider;
  9.     }
  10.     public IActionResult Index()
  11.     {
  12.         var viewdata = new ViewDataDictionary<FooModel>(_modelMetadataProvider);
  13.         viewdata.Model = new FooModel();
  14.         return _actionHelper.View("Index", viewdata);
  15.     }
  16. }

Ahora el controlador solo toma una dependencia contra el IActionResultHelper y el IModelMetadataProvider. Las dependencias contra el IServiceProvider y el IViewEngine (que eran solo para crear el ViewResult) son gestionadas ahora por el IActionResultHelper.

¡Y listos! Hemos visto como podemos crear controladores POCO (que no hereden de Controller) y como a través de la inyección de dependencias ¡recibimos las dependencias necesarias de forma automática!

¡Saludos!

Lentamente se van desgranando las novedades que incorporará ASP.NET vNext. Recordad que podéis ya jugar un poco con él, descargandoos la CTP de VS14 :)

Vamos a ver en este post cuatro cosillas sobre el routing que incorpora ASP.NET vNext. Vamos a partir de una aplicación web vNext vacía y agregaremos tan solo lo justo para tener MVC y una tabla de rutas. Lo primero será editar el project.json y añadir la referencia a asp.net MVC:

  1. "dependencies": {
  2.     "Helios" : "0.1-alpha-build-0585",
  3.     "Microsoft.AspNet.Mvc": "0.1-alpha-build-1268"
  4. },

Una vez hecho esto ya podemos editar el método Configure de la clase Startup para agregar MVC y configurar una tabla de rutas:

  1.     app.UseServices(s => s.AddMvc());
  2.     app.UseMvc(r =>
  3.     {
  4.         r.MapRoute(
  5.             name: "default",
  6.             template: "{controller}/{action}/{id?}",
  7.             defaults: new { controller = "Home", action = "Index" });
  8.     });
  9. }

Con esto tenemos la “clásica” tabla de rutas por defecto. Una diferencia respecto a MVC clásico (MVC5 y anteriores) es el uso de id? para indicar un parámetro opcional (en lugar de colocar id = UrlParameter.Optional en el defaults). Esta nomenclatura es la misma que usábamos en el routing attribute de webapi 2 y es mucho más concisa.

Si añadimos un controlador HomeController para que nos devuelva una vista (Index.cshtml) veremos que todo funciona correctamente… Hasta ahí nada nuevo bajo el sol.

Pero, recordad: WebApi y MVC están ahora unificados y a nivel de routing funcionaban de forma relativamente distinta. Pero… ¿como funcionará el routing de vNext? Hagamos un experimento:

  1. public IActionResult Index()
  2.  {
  3.      return View();
  4.  }
  5.  
  6.  public IActionResult Index(int id)
  7.  {
  8.      return View();
  9.  }

En MVC clásico este código no funciona (MVC se queja de una ambigüedad) ya que dos métodos de un controlador no pueden implementar la misma acción (para el mismo verbo HTTP). Pero en WebApi esto funciona. Pues en vNext también.

Si invocamos a la acción sin pasar el parámetro id (p. ej. con / o con /Home/Index) se ejecutará el primer método. Si invocamos la acción pasando el parámetro id (p. ej. con /Home/Index/2) se ejecutará el segundo método.

Prosigamos nuestro experimento… Modifiquemos el método Startup.Configure para tener la siguiente tabla de rutas:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product", action="Display"});
  12. });

Creamos el controlador ProductController con una acción Dispay:

  1. public IActionResult Display(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View();
  6. }

Si ahora vamos a /Details/100.. ¿qué es lo que obtenemos? En ASP.NET MVC clásico obtendremos un 404 porque las rutas se evalúan en orden y la primera que es posible aplicar se aplica. Así la url /Details/100 encaja dentro de la primera ruta con:

  • controller = Details
  • action = 100
  • id = sin valor

Y como no existe el DetailsController de ahí el 404 que obtendríamos con MVC clásico. De ahí que la tabla de rutas tenga que configurarse con las rutas en orden desde la más específica a la más general.

Pero en vNext eso ya no es así. Las rutas siguen evaluándose en orden pero si una ruta devuelve un 404 el control pasa a la siguiente ruta. Así, en este caso se intenta evaluar la primera ruta con los parámetros indicados anteriormente (al igual que en MVC clásico). Dicha ruta devuelve un 404 así que el control pasa a la siguiente ruta. La URL /Details/10 encaja con el patrón de la siguiente ruta así que se intenta aplicar dicha ruta con los parámetros:

  • controller = Product (sacado de defaults)
  • action = Display (scado de defaults)
  • id = 100

Ahora como el controlador y la acción existen se invocan y vemos la página de detalles del producto 100.

Hagamos una tercera modificación a la tabla de rutas para que quede así:

  1. app.UseMvc(r =>
  2. {
  3.     r.MapRoute(
  4.         name: "default",
  5.         template: "{controller}/{action}/{id?}",
  6.         defaults: new { controller = "Home", action = "Index" });
  7.  
  8.     r.MapRoute(
  9.         name: "second",
  10.         template: "Details/{id?}",
  11.         defaults: new { controller = "Product"});
  12. });

Es la misma tabla de rutas que teníamos con una excepción importante: la ruta “second” no define acción.

Si ahora navegamos de nuevo a /Details/100 obtenemos un 404. Era de esperar, puesto que no hay acción alguna… Pero el routing de vNext nos tiene reservada una última sorpresa: si no hay acción definida se enrutará por el verbo HTTP.

Para hacer la prueba modifica el controlador Product:

  1. public IActionResult Get(int? id)
  2. {
  3.     if (id == null) ViewBag.Pid = "None";
  4.     else ViewBag.Pid = id;
  5.     return View("Display");
  6. }

Si ahora navegas a /Details/100 deberías ver, de nuevo, los detalles del producto con id 100. Al no haber acción vNext ha enrutado por el verbo HTTP y nuestro controlador se ha enrutado como si fuese un controlador de webapi.

Bueno… hemos visto brevemente algunas de las diferencias entre el routing de vNext y el de MVC tradicional. Para más detalles os recomiendo este post del equipo de ASP.NET.

Saludos!

Muy buenas! Acabo de leer el post de Luis Ruiz Pavon sobre sobre el sobreescribir .ToString() para mejorar la información del modo de depuración de VS.

Y ya puestos, para complementar su post, quería comentar un truco que no se si conoce mucha gente y que permite algo parecido sin necesidad de sobreescribir ToString (que se usa para otras cosas además de para mostrar la información en la ventana de depuración) y que es el uso del atributo DebuggerDisplay.

En este caso un ejemplo vale más que mil palabras:

  1.    [DebuggerDisplay ("Product {Name} ({Code}): {Description}")]
  2.     public class Product
  3.     {
  4.         public string Code { get; set; }
  5.         public string Name { get; set; }
  6.         public string Description { get; set; }
  7.         public Product(string code, string name, string description)
  8.         {
  9.             Code = code;
  10.             Name = name;
  11.             Description = description;
  12.         }
  13.         public override string ToString()
  14.         {
  15.             return String.Format("Code - {0}", Code);
  16.         }
  17.     }

Y el resultado en la ventana de watches es:

image

Podemos conseguir cosas más interesantes utilizando las propiedades Name y TargetTypeName que modifican los valores de las columnas “Name” y “Type”. Así si uso el siguiente [DebuggerDisplay]:

  1. [DebuggerDisplay ("({Code}): {Description}", Name = "Product {Name}", Type ="Producto")]

Veo lo siguiente en la ventana de watches:

image

Y en la ventana de Locals veo, ahora, lo siguiente:

image

El uso de DebuggerDisplay tiene sus limitaciones si se compara con redefinir ToString() (este último es un método y puede contener código como el que mostraba Luis en su post para convertir a JSON), mientras que DebuggerDisplay tan solo puede generar una pequeña cadena a partir de las propiedades.

Si una clase tiene tanto ToString() redefinido como DebuggerDispay este último tiene precedencia para el depurador.

¡Y… eso es todo! :)

Saludos!

Más artículos Página siguiente >