Ejercicio: Consumo de un servicio REST con HttpClient
Como parte de la aplicación que usan los ingenieros en las visitas al sitio de los clientes, debe agregar una característica que permita a un ingeniero buscar los detalles de los componentes eléctricos. Esta información se mantendrá en una base de datos y se podrá acceder a ella a través de un servicio web REST. También se le ha pedido que proporcione una interfaz que permita a un administrador crear, quitar y modificar los detalles de las piezas guardadas en la base de datos con el mismo servicio web REST.
En este ejercicio implementará el servicio web REST en Azure y, a continuación, comprobará que puede obtener acceso a él con un explorador web. A continuación, agregará funcionalidades a una aplicación existente que usa el servicio web REST para recuperar, agregar, eliminar y actualizar los detalles de los componentes eléctricos.
Realizará este ejercicio con el espacio aislado de Azure.
Sugerencia
Puede usar el botón Copiar para copiar los comandos en el Portapapeles. Para pegarlos, haga clic con el botón derecho en una nueva línea en el terminal de Cloud Shell y seleccione Pegar, o bien use el método abreviado de teclado Mayús+Insert (⌘+V en macOS).
Implementar el servicio web REST de Piezas
En la ventana Cloud Shell, ejecute el siguiente comando para clonar el repositorio que contiene el código para este ejercicio, incluido el servicio web REST Piezas:
git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
Vaya a la carpeta Consume-REST-services:
cd mslearn-dotnetmaui-consume-rest-services/src
Ejecute el comando siguiente para implementar el servicio web Piezas con el espacio aislado de Azure Cloud Shell. Este comando hace que el servicio esté disponible a través de una dirección URL única. Cuando esta dirección URL se muestre, tome nota de ella. Configurará la aplicación para que se conecte al servicio web con esta dirección URL.
bash initenvironment.sh
Examinar el código del servicio web
Nota:
Realizará el resto de este ejercicio en el equipo de desarrollo local.
En el equipo, abra una ventana del símbolo del sistema y clone el repositorio para este ejercicio. El código está en el repositorio net-maui-learn-consume-rest-services.
git clone https://github.com/microsoftdocs/mslearn-dotnetmaui-consume-rest-services
Nota:
Es mejor clonar o descargar el contenido del ejercicio en una ruta de acceso de carpeta corta, como C:\dev, para evitar que los archivos generados por compilación excedan la longitud máxima de la ruta de acceso.
Vaya a la carpeta src\webservice\PartsServer del clon del repositorio y abra la solución PartsServer.sln con Visual Studio o la carpeta en Visual Studio Code. Esta solución contiene una copia del código del servicio web que ha implementado en Azure en el procedimiento anterior.
En la ventana Explorador de soluciones, expanda la carpeta Models. Esta carpeta contiene dos archivos:
Part.cs. La clase Part representa un elemento según lo proporcionado por el servicio web REST. Los campos incluyen el Id., el nombre, el tipo, la fecha de disponibilidad (la primera vez que se ha suministrado la pieza) y una lista de proveedores de pieza. La propiedad Href devuelve el URI relativo de la pieza; un cliente REST puede usar este URI para hacer referencia a esta parte específica en el servicio web REST. La propiedad Suppliers devuelve la lista de proveedores de la pieza como una cadena.
PartsFactory.cs. La clase PartsFactory inicializa la lista de piezas proporcionadas por el servicio, usando un pequeño conjunto de valores codificados de forma rígida. En el mundo real estos datos se recuperarían de una base de datos.
En la ventana Explorador de soluciones, expanda la carpeta Controllers. Esta carpeta contiene los archivos siguientes:
PartsController.cs. La clase PartsController implementa la API web para el servicio. Incluye métodos que permiten a una aplicación cliente recuperar una lista de todas las piezas (Get), encontrar los detalles de una pieza específica dada la Id. de pieza (la versión sobrecargada de Get), actualizar los detalles de una pieza (Put), agregar una nueva pieza a la lista (Post) y quitar una pieza de la lista (Delete).
LoginController.cs. La clase LoginController implementa una forma sencilla de autenticación para el servicio web. Una aplicación debe enviar una solicitud HTTP GET a este controlador, que devuelve un token de autorización. Este token de autorización se usa para autenticar las solicitudes enviadas al PartsController.
BaseController.cs. La clase BaseController contiene la lógica que se usa para autenticar solicitudes. La clase PartsController hereda de esta clase. Si el cliente intenta llamar a métodos en la clase PartsController sin proporcionar un token de autenticación válido, los métodos devuelven una respuesta HTTP 401 (no autorizado).
Examinar el código de la aplicación cliente .NET MAUI
En este módulo se usa el SDK de .NET 8.0. Asegúrese de que tiene instalado .NET 8.0 mediante la ejecución del siguiente comando en el terminal de comandos que prefiera:
dotnet --list-sdks
Aparecerá un resultado similar al del ejemplo siguiente:
6.0.317 [C:\Program Files\dotnet\sdk]
7.0.401 [C:\Program Files\dotnet\sdk]
8.0.100 [C:\Program Files\dotnet\sdk]
Asegúrese de que aparezca una versión que comience en 8
. Si no aparece ninguna o no se encuentra el comando, instale el SDK más reciente de .NET 8.0.
Cierre la solución PartsServer y abra la solución PartsClient en la carpeta src\client\PartsClient del repositorio clonado. Esta solución contiene una implementación parcial de una aplicación cliente .NET MAUI que usa el servicio web PartsServer.
En la ventana Explorador de soluciones, expanda la carpeta Datos. Esta carpeta contiene el código de dos clases:
PartsManager.cs. La clase PartsManager proporciona los métodos que usa la aplicación cliente para interactuar con el servicio web REST. Esta clase está incompleta actualmente; deberá agregar el código necesario durante este ejercicio. Cuando se completa, el método GetClient se conecta al servicio web REST. El método GetAll devuelve una lista de piezas del servicio web REST. El método Add agrega una nueva pieza a la lista de piezas administradas por el servicio web REST. El método Update modifica los detalles de una pieza almacenada por el servicio web REST y el método Delete quita una pieza.
Part.cs. La clase Part modela una pieza almacenada en la base de datos. Expone propiedades que una aplicación puede usar para tener acceso a los campos PartID, PartName, PartAvailableDate, PartType y PartSuppliers. La clase también proporciona un método de utilidad denominado SupplierString que una aplicación puede usar para recuperar una cadena con formato que contiene los nombres de proveedor.
En la ventana Explorador de soluciones, expanda la carpeta Pages. Esta carpeta contiene el marcado y el código de dos páginas:
PartsPage.xaml. En esta página se usa un diseño CollectionView con una plantilla DataTemplate para mostrar los detalles de las piezas disponibles como una lista. DataTemplate usa el enlace de datos para conectar los datos mostrados a las piezas recuperadas del servicio web. Puede seleccionar una fila de CollectionView para editar una pieza de AddPartPage, o bien puede seleccionar el botón Agregar nueva pieza para agregar una pieza nueva.
AddPartPage.xaml. Esta página permite a los usuarios escribir y guardar los detalles de una pieza nueva. Los usuarios pueden especificar el nombre de la pieza, el tipo de pieza y un proveedor inicial. El Id. de pieza y la fecha de disponibilidad de la pieza se generan automáticamente.
En la ventana Explorador de soluciones, expanda la carpeta ViewModels. Esta carpeta contiene dos clases: AddPartViewModel.cs y PartsViewModel.cs. Estos son los modelos de vista de sus respectivas páginas y contienen propiedades y la lógica que necesita la página para mostrar y manipular datos.
Inicio de sesión en el servicio
El servicio REST requiere que inicie sesión primero para obtener un token de autorización. No hay autenticación de usuarios. Primero se llama a un punto de conexión específico para obtener un token de autorización y, a continuación, se devuelve el token al servidor en cada solicitud posterior en el encabezado HTTP.
Abra el archivo PartsManager.cs en la carpeta Data.
Agregue los campos estáticos BaseAddress y Url a la clase PartsManager tal y como se define en el siguiente fragmento de código. Reemplace el texto URL GOES HERE con la dirección URL del servicio web REST que ha anotado anteriormente:
public class PartsManager { static readonly string BaseAddress = "URL GOES HERE"; static readonly string Url = $"{BaseAddress}/api/"; ... }
Agregue el siguiente campo a la clase, después del campo Url. Este campo retendrá el token de autorización devuelto cuando el usuario inicia sesión:
private static string authorizationKey;
Busque el método GetClient. Actualmente, este método produce una excepción NotImplementedException. Reemplace el código existente en este método por el código siguiente. Este código crea un objeto HttpClient y, a continuación, envía una solicitud al punto de conexión de inicio de sesión del servicio web REST. El servicio debe responder con un mensaje que contenga el token de autorización. Deserialize este token y agréguelo como encabezado de solicitud de autorización predeterminado para las solicitudes siguientes enviadas mediante el objeto HttpClient:
private static async Task<HttpClient> GetClient() { if (client != null) return client; client = new HttpClient(); if (string.IsNullOrEmpty(authorizationKey)) { authorizationKey = await client.GetStringAsync($"{Url}login"); authorizationKey = JsonSerializer.Deserialize<string>(authorizationKey); } client.DefaultRequestHeaders.Add("Authorization", authorizationKey); client.DefaultRequestHeaders.Add("Accept", "application/json"); return client; }
Realizar una operación GET para recuperar información de piezas
En el archivo PartsManager.cs busque el método GetAll. Este es un método asincrónico que devuelve una lista de piezas que se puede enumerar. Este método aún no está implementado.
En este método elimine el código que produce la excepción NotImplementedException.
Compruebe si el dispositivo tiene conectividad a Internet mediante la clase
Connectivity
. Si no hay Internet, se devuelve un objetoList<Part>
vacío.if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new List<Part>();
Llame al método GetClient para recuperar un objeto HttpClient con el que trabajar. Recuerde que GetClient es asincrónico, por lo que debe usar el operador await para capturar el objeto devuelto por este método.
Llame al método GetStringAsync del objeto HttpClient y proporcione la dirección URL base para recuperar una matriz de piezas del servicio web REST. Los datos se devuelven asincrónicamente como una cadena JSON.
Deserialize la cadena JSON devuelta por este método en una lista de objetos Part mediante el método JsonSerializer.Deserialize. Devuelva esta lista al autor de la llamada.
El método completado debería tener el siguiente aspecto:
public static async Task<IEnumerable<Part>> GetAll() { if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new List<Part>(); var client = await GetClient(); string result = await client.GetStringAsync($"{Url}parts"); return JsonSerializer.Deserialize<List<Part>>(result, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }); }
Compile y ejecute la aplicación. Cuando se inicia la aplicación, aparece la página Lista de piezas, y una lista de piezas recuperadas por el método GetAll debería aparecer. La imagen siguiente muestra la aplicación cuando se ejecuta en Android:
Cuando haya terminado de explorar los datos, cierre la aplicación y vuelva a Visual Studio o Visual Studio Code.
Realizar una operación POST para agregar una nueva pieza a la base de datos
En la clase PartsManager, busque el método Add. Este método tiene parámetros para el nombre de la pieza, un proveedor y el tipo de pieza. El método es asincrónico. El propósito del método es insertar una nueva pieza en la base de datos y devolver un objeto Part que representa el elemento recién creado.
Elimine el código existente en el método.
Compruebe si el dispositivo tiene conectividad a Internet mediante la clase
Connectivity
. Si no hay Internet, se devuelve un objetoPart
vacío.if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return new Part();
Cree un nuevo objeto Part. Rellene los campos con los datos pasados:
- Establezca el campo PartID en una cadena vacía. El servicio web REST generará este identificador.
- Cree una nueva List para conservar el nombre del proveedor.
- Establezca el campo PartAvailableDate en DateTime.Now.
- Obtenga un cliente HTTP desde el método GetClient.
var part = new Part() { PartName = partName, Suppliers = new List<string>(new[] { supplier }), PartID = string.Empty, PartType = partType, PartAvailableDate = DateTime.Now.Date };
Llame al método GetClient para recuperar un objeto HttpClient con el que trabajar.
Cree un objeto
HttpRequestMessage
. Este objeto se usa para modelar la solicitud que se envía al servicio web. Inícielo con parámetros que indiquen el verbo HTTP que se usará y la dirección URL del servicio web con el que se va a comunicar.var msg = new HttpRequestMessage(HttpMethod.Post, $"{Url}parts");
Debe enviar una carga al servicio web con la información de la Part que se va a crear. Esta carga se serializará en JSON. La carga de JSON se agrega a la propiedad
HttpRequestMessage.Content
y se serializa con el métodoJsonContent.Create
.msg.Content = JsonContent.Create<Part>(part);
Ahora, envíe el mensaje al servicio web con la función
HttpClient.SendAsync
. Esa función devolverá un objetoHttpResponseMessage
que contiene información sobre la operación en el servidor. Como, por ejemplo, códigos de respuesta HTTP e información pasada desde el servidor.var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
Tenga en cuenta que en el código anterior se usa el método
response.EnsureSuccessStatusCode
. Esto producirá un error si se devuelve algo distinto que un código de estado HTTP 2xx.Si el servicio web devuelve información, como un objeto serializado en JSON, puede leerlo en
HttpResponseMessage
. Luego, puede deserializar el JSON medianteJsonSerializer.Deserialize
.var returnedJson = await response.Content.ReadAsStringAsync(); var insertedPart = JsonSerializer.Deserialize<Part>(returnedJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, });
Por último, devuelva la nueva Part insertada.
return insertedPart;
Compile y ejecute la aplicación. Seleccione el botón Add New Part y escriba un nombre, un tipo y un proveedor para crear una nueva pieza. Seleccione Guardar. Se invocará el método Add de la clase PartsManager, que crea la pieza nueva en el servicio web. Si la operación se realiza correctamente, la página de lista de piezas volverá a aparecer con la pieza nueva en la parte inferior de la lista.
Cuando haya terminado de explorar los datos, cierre la aplicación y vuelva a Visual Studio o Visual Studio Code.
Realizar una operación PUT para actualizar los detalles de una pieza de la base de datos
En la clase PartsManager busque el método Update. Este es un método asincrónico que toma un objeto Part como parámetro. El método no tiene un valor de devolución explícito. Sin embargo, el tipo devuelto es Task, para que las excepciones se devuelvan correctamente al autor de la llamada. Vamos a implementar la funcionalidad PUT.
Elimine el código existente.
Como antes, compruebe si hay conexión a Internet.
if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return;
Cree un nuevo
HttpRequestMessage
, esta vez especificando una operación PUT y la dirección URL para actualizar piezas.HttpRequestMessage msg = new(HttpMethod.Put, $"{Url}parts/{part.PartID}");
Establezca la propiedad
Content
delHttpRequestMessage
que usa la funciónJsonContent.Create
y el parámetro part que se ha pasado a la función.msg.Content = JsonContent.Create<Part>(part);
Obtenga un cliente HTTP desde el método GetClient.
var client = await GetClient();
Envíe la solicitud con el
HttpClient
y, a continuación, asegúrese de que se ha realizado correctamente.var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
Compile y ejecute la aplicación. Seleccione una de las piezas de la lista. Aparecerá la página AddPart, esta vez con las propiedades ya rellenadas. Actualice algo.
Seleccione Guardar. Esto llama al método Update de la clase PartsManager para enviar los cambios al servicio web. Si se realiza correctamente, la página de lista de piezas volverá a aparecer y se reflejarán los cambios.
Nota:
La pieza que se ha agregado en la tarea anterior no aparecerá en la página Part List. Los datos que usa la aplicación se restablecen a una lista de piezas predefinidas cada vez que se ejecuta la aplicación. De este modo, se proporciona coherencia para probar la aplicación.
Realizar una operación DELETE para quitar los detalles de una pieza de la base de datos
En la clase PartsManager, busque el método Delete. Este es un método asincrónico que toma una cadena partId y devuelve una Task.
Elimine el código existente.
Busque una conexión a Internet.
if (Connectivity.Current.NetworkAccess != NetworkAccess.Internet) return;
Cree un nuevo objeto
HttpRequestMessage
. Solo ahora se especifica el verbo DELETE HTTP y la dirección URL para eliminar un elemento.HttpRequestMessage msg = new(HttpMethod.Delete, $"{Url}parts/{partID}");
Obtenga un cliente HTTP desde el método GetClient.
var client = await GetClient();
Envíe la solicitud al servicio web. Compruebe si el resultado es correcto cuando vuelva.
var response = await client.SendAsync(msg); response.EnsureSuccessStatusCode();
Compile y ejecute la aplicación. Seleccione un elemento de la lista y, a continuación, seleccione Eliminar en la página Agregar pieza. Si se realiza correctamente, la página Part Listvolverá a aparecer y la parte eliminada ya no estará visible.