Azure Mobile Services nos proporciona servicios de backend en la nube para Windows Phone, Windows 8, iOS, Android y plataformas basadas en web. Permite además agregar seguridad, autenticación y notificaciones de manera sencilla y rápida. Pero, cómo funciona realmente?
Para averiguarlo, podemos recurrir a la documentación que tenemos en MSDN, aunque también podemos ver directamente el código fuente.
La API que se nos ofrece es open source, con lo cual podemos no solamente ver el código fuente sino además poder realizar contribuciones al mismo.
En este artículo quiero hacer un breve recorrido por la API, para una operación muy concreta, insertar elementos. En otros artículos de esta serie veremos cómo hacer consultas, o qué pasa del lado de servidor.
Para este artículo usaremos los siguientes elementos:
- Tutorial de datos
- Documentación de la API en MSDN
- Código fuente del SDK en Github
Los ejemplos los veremos con el SDK de Windows Phone 8, pero podemos ver que existe un código similar para el resto de plataformas compatibles.
De un vistazo
La API de Azure Mobile Services está compuesto por objetos que nos ayudan a:
- Convertir nuestros objetos a formato JSON para su envío y hacer la operación contraria a la recepción (Serialización)
- Realizar solicitudes al servicio que hemos creado en Azure mediante comandos HTTP usando una arquitectura REST
- Gestionar autentificación y seguridad para las mismas.
El código fuente del SDK para Windows Phone 8 contiene varios elementos
- Tests que aseguran su integridad (a nivel de core como a nivel de cada cliente en particular).
- Código manejado (es decir, código en .NET tradicional).
- Código nativo, dentro de los espacios de nombres de WinMD (Recordemos que Windows Phone 8 tiene soporte para las API nativas de WinRT).
Para poder trabajar con Mobile Services necesitamos en primer lugar una referencia a la tabla:
private MobileServiceCollection<TodoItem, TodoItem> items; private IMobileServiceTable<TodoItem> todoTable = App.MobileService.GetTable<TodoItem>();
Este método está situado en la clase MobileServiceClient que contiene otros que veremos a lo largo del artículo:
Este método está situado en la siguiente ruta:
src/ Microsoft.Azure.Zumo.WindowsPhone8.Managed/ Core/ MobileServiceClient.Managed.cs
El código es el siguiente:
public IMobileServiceTable GetTable() { SerializableType type = SerializableType.Get(typeof(T)); return new MobileServiceTable(type.TableName, this); }
En primer lugar obtiene un tipo serializable específico, que es el que se usará para poder transformar el objeto en una tabla en el servidor. Luego generará una tabla de tipo MobileServiceTable con el nombre creado a partir de este tipo. Hasta aquí solamente hemos hecho trabajo en local, es decir, no ha habido conexión con el servicio.
Veamos qué pasa al insertar un elemento en la tabla:
await todoTable.InsertAsync(todoItem);
El código para esta función se encuentra en la siguiente ruta:
src/ Microsoft.Azure.Zumo.WindowsPhone8.Managed/ Core/ MobileServiceTable.Generic.cs
En este código lo que vemos es lo siguiente:,
- No podemos insertar elementos nulos,
- Se obtiene un JObject (un objeto serializado en formato JSON) a partir de MobileServiceTableSerializer
- Se envía una petición ya con el objeto serializado
- Se recibe el resultado de la petición (en formato JSON)
- Finalmente, se convierte el objeto recibido en el enviado, por si los datos hubiesen cambiado.
public async Task InsertAsync(T instance) { if (instance == null) { throw new ArgumentNullException("instance"); } // Serialize the instance JObject value = MobileServiceTableSerializer.Serialize(instance).AsObject(); // Send the request JToken response = await this.InsertAsync(value); // Deserialize the response back into the instance in case any // server scripts changed values of the instance. MobileServiceTableSerializer.Deserialize(response, instance); }
El envío de la petición se hace en dos fases, una fase manejada, y otra fase nativa, la fase manejada se hace en un fichero situado en:
src/ Microsoft.Azure.Zumo.WindowsPhone8.Managed/ Core/ MobileServiceClient.Managed.cs
En el que se realiza la siguiente operación:
public Task<JToken> InsertAsync(JObject instance) { return this.SendInsertAsync(instance); }
Que nos lleva a este otro fichero (situado en la fase manejada):
internal async Task SendInsertAsync(JObject instance) { if (instance == null) { throw new ArgumentNullException("instance"); } // Make sure the instance doesn't have its ID set for an insertion if (instance.Get(IdPropertyName) != null) { throw new ArgumentException( string.Format( CultureInfo.InvariantCulture, Resources.CannotInsertWithExistingIdMessage, IdPropertyName), "instance"); } string url = GetUriFragment(this.TableName); JToken response = await this.MobileServiceClient.RequestAsync("POST", url, instance); JToken patched = Patch(instance, response); return patched; }
Aquí las operaciones realizadas son:
- Comprobar que el elemento que estamos insertando no tenga ya un Id, al fin y al cabo es nuevo.
- Obtener la url de la tabla a la que vamos a insertar el elemento.
- Enviar una petición HTTP POST al servicio web, con la url que hemos especificado y los datos, y recuperar el resultado.
- Actualizar el objeto enviado con la respuesta.
- Devolver el objeto enviado.
Finalmente llegamos a RequestAsync, donde se realiza la petición, situada en este fichero:
src/ Microsoft.Azure.Zumo.WindowsPhone8.Managed/ Core/ MobileServiceClient.cs
Este método genera una petición del tipo deseado, especifica que solamente queremos parámetros en formato JSON, especifica la autenticación a partir de las claves que hemos especificado en la configuración inicial, y finalmente envía la petición:
internal async Task<JToken> RequestAsync(string method, string uriFragment, JToken content) { Debug.Assert(!string.IsNullOrEmpty(method), "method cannot be null or empty!"); Debug.Assert(!string.IsNullOrEmpty(uriFragment), "uriFragment cannot be null or empty!"); // Create the web request IServiceFilterRequest request = new ServiceFilterRequest(); request.Uri = new Uri(this.ApplicationUri, uriFragment); request.Method = method.ToUpper(); request.Accept = RequestJsonContentType; // Set Mobile Services authentication, application, and telemetry // headers request.Headers[RequestInstallationIdHeader] = applicationInstallationId; if (!string.IsNullOrEmpty(this.ApplicationKey)) { request.Headers[RequestApplicationKeyHeader] = this.ApplicationKey; } if (!string.IsNullOrEmpty(this.currentUserAuthenticationToken)) { request.Headers[RequestAuthenticationHeader] = this.currentUserAuthenticationToken; } // Add any request as JSON if (content != null) { request.ContentType = RequestJsonContentType; request.Content = content.ToString(); } // Send the request and get the response back as JSON IServiceFilterResponse response = await ServiceFilter.ApplyAsync(request, this.filter); JToken body = GetResponseJsonAsync(response); // Throw errors for any failing responses if (response.ResponseStatus != ServiceFilterResponseStatus.Success || response.StatusCode >= 400) { ThrowInvalidResponse(request, response, body); } return body; }
Es todo un conjunto de acciones que se llevan a cabo solamente para insertar un elemento. En el próximo artículo veremos cómo se ejecutan las consultas usando la API, y cómo funcionan métodos como este:
List<TodoItem> items = await todoTable .Where(todoItem => todoItem.Complete == false) .ToListAsync();
Happy Hacking!