Consumo de un servicio web ASP.NET (ASMX)
ASMX proporciona la capacidad de crear servicios web que envíen mensajes mediante el Protocolo simple de acceso a objetos (SOAP). SOAP es un protocolo independiente de la plataforma e independiente del lenguaje para compilar servicios web y acceder a ellos. Los consumidores de un servicio ASMX no necesitan saber nada sobre la plataforma, el modelo de objetos o el lenguaje de programación que se usa para implementar el servicio. Solo necesitan saber cómo enviar y recibir mensajes SOAP. En este artículo se muestra cómo consumir un servicio SOAP ASMX desde una aplicación Xamarin.Forms.
Un mensaje SOAP es un documento XML que contiene los siguientes elementos:
- Elemento raíz denominado Envelope que identifica el documento XML como un mensaje SOAP.
- Un elemento opcional Header que contiene información específica de la aplicación, como los datos de autenticación. Si el elemento Header está presente, debe ser el primer elemento secundario del elemento Envelope.
- Elemento Body necesario que contiene el mensaje SOAP destinado al destinatario.
- Elemento Fault opcional que se usa para indicar mensajes de error. Si el elemento Fault está presente, debe ser un elemento secundario del elemento Body.
SOAP puede funcionar a través de muchos protocolos de transporte, como HTTP, SMTP, TCP y UDP. Sin embargo, un servicio ASMX solo puede funcionar a través de HTTP. La plataforma Xamarin admite implementaciones estándar de SOAP 1.1 a través de HTTP y esto incluye compatibilidad con muchas de las configuraciones de servicio ASMX estándar.
Este ejemplo incluye las aplicaciones móviles que se ejecutan en dispositivos físicos o emulados, y un servicio ASMX que proporciona métodos para obtener, agregar, editar y eliminar datos. Cuando se ejecutan las aplicaciones móviles, se conectan al servicio ASMX hospedado localmente, como se muestra en la captura de pantalla siguiente:
Nota:
En iOS 9 y versiones posteriores, App Transport Security (ATS) exige conexiones seguras entre los recursos de Internet (como el servidor back-end de la aplicación) y la aplicación, lo que impide la divulgación accidental de información confidencial. Dado que ATS está habilitado de forma predeterminada en las aplicaciones compiladas para iOS 9, todas las conexiones estarán sujetas a los requisitos de seguridad de ATS. Si las conexiones no cumplen estos requisitos, producirán un error con una excepción.
ATS puede desactivarse si no es posible usar el protocolo HTTPS
y la comunicación segura para los recursos de Internet. Esto se puede lograr actualizando el archivo Info.plist de la aplicación. Para obtener más información, consulte Seguridad de transporte de aplicación.
Consumo del servicio web
El servicio ASMX proporciona las siguientes operaciones:
Operación | Descripción | Parámetros |
---|---|---|
GetTodoItems | Obtención de una lista de tareas pendientes | |
CreateTodoItem | Crear un nuevo elemento de tareas pendientes | Un objeto TodoItem serializado XML |
EditTodoItem | Actualizar una tarea pendiente | Un objeto TodoItem serializado XML |
DeleteTodoItem | Eliminar una tarea pendiente | Un objeto TodoItem serializado XML |
Para obtener más información sobre el modelo de datos usado en la aplicación, vea Modelado de los datos.
Creación del proxy TodoService
Una clase de proxy, denominada TodoService
, extiende SoapHttpClientProtocol
y proporciona métodos para comunicarse con el servicio ASMX a través de HTTP. El proxy se genera agregando una referencia web a cada proyecto específico de la plataforma en Visual Studio 2019 o Visual Studio 2017. La referencia web genera métodos y eventos para cada acción definida en el documento lenguaje de descripción de servicios web (WSDL) del servicio.
Por ejemplo, la acción GetTodoItems
servicio da como resultado un método GetTodoItemsAsync
y un evento GetTodoItemsCompleted
en el proxy. El método generado tiene un tipo de valor devuelto void e invoca la GetTodoItems
acción en la clase primaria SoapHttpClientProtocol
. Cuando el método invocado recibe una respuesta del servicio, desencadena el evento GetTodoItemsCompleted
y proporciona los datos de respuesta dentro de la propiedad Result
del evento.
Creación de la implementación de ISoapService
Para permitir que el proyecto compartido y multiplataforma funcione con el servicio, el ejemplo define la ISoapService
interfaz, que sigue el Modelo de programación asincrónica de tareas en C#. Cada plataforma implementa para ISoapService
exponer el proxy específico de la plataforma. En el ejemplo se usan TaskCompletionSource
objetos para exponer el proxy como una interfaz asincrónica de tareas. Los detalles sobre el uso TaskCompletionSource
se encuentran en las implementaciones de cada tipo de acción en las secciones siguientes.
El ejemplo SoapService
:
- Crea instancias
TodoService
como una instancia de nivel de clase - Crea una colección denominada
Items
para almacenarTodoItem
objetos - Especifica un punto de conexión personalizado para la propiedad opcional
Url
en elTodoService
public class SoapService : ISoapService
{
ASMXService.TodoService todoService;
public List<TodoItem> Items { get; private set; } = new List<TodoItem>();
public SoapService ()
{
todoService = new ASMXService.TodoService ();
todoService.Url = Constants.SoapUrl;
...
}
}
Creación de objetos de transferencia de datos
La aplicación de ejemplo usa la clase TodoItem
para modelar datos. Para almacenar un elemento TodoItem
en el servicio web, primero debe convertirse al tipo generado por proxy TodoItem
. Esto se logra mediante el método ToASMXServiceTodoItem
, como se muestra en el ejemplo de código siguiente:
ASMXService.TodoItem ToASMXServiceTodoItem (TodoItem item)
{
return new ASMXService.TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}
Este método crea una nueva ASMService.TodoItem
instancia y establece cada propiedad en la propiedad idéntica de la TodoItem
instancia.
De forma similar, cuando los datos se recuperan del servicio web, se deben convertir del tipo generado TodoItem
por proxy a una TodoItem
instancia. Esto se logra con el método FromASMXServiceTodoItem
, como se muestra en el ejemplo de código siguiente:
static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)
{
return new TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}
Este método recupera los datos del tipo generado por TodoItem
proxy y lo establece en la instancia recién creada TodoItem
.
Recuperación de datos
La interfaz ISoapService
espera que el método RefreshDataAsync
devuelva un Task
con la colección de elementos. Sin embargo, el método TodoService.GetTodoItemsAsync
devuelve void. Para satisfacer el patrón de interfaz, debe llamar a GetTodoItemsAsync
, esperar a que se active el GetTodoItemsCompleted
evento y rellenar la colección. Esto le permite devolver una colección válida a la interfaz de usuario.
En el ejemplo siguiente se crea un nuevo TaskCompletionSource
, se inicia la llamada asincrónica en el RefreshDataAsync
método y se espera el Task
proporcionado por el TaskCompletionSource
. Cuando se invoca el controlador de eventos TodoService_GetTodoItemsCompleted
, rellena la colección Items
y actualiza el TaskCompletionSource
:
public class SoapService : ISoapService
{
TaskCompletionSource<bool> getRequestComplete = null;
...
public SoapService()
{
...
todoService.GetTodoItemsCompleted += TodoService_GetTodoItemsCompleted;
}
public async Task<List<TodoItem>> RefreshDataAsync()
{
getRequestComplete = new TaskCompletionSource<bool>();
todoService.GetTodoItemsAsync();
await getRequestComplete.Task;
return Items;
}
private void TodoService_GetTodoItemsCompleted(object sender, ASMXService.GetTodoItemsCompletedEventArgs e)
{
try
{
getRequestComplete = getRequestComplete ?? new TaskCompletionSource<bool>();
Items = new List<TodoItem>();
foreach (var item in e.Result)
{
Items.Add(FromASMXServiceTodoItem(item));
}
getRequestComplete?.TrySetResult(true);
}
catch (Exception ex)
{
Debug.WriteLine(@"\t\tERROR {0}", ex.Message);
}
}
...
}
Para obtener más información, vea Modelo de programación asincrónica y TPL y programación asincrónica tradicional de .NET Framework.
Crear o editar datos
Al crear o editar datos, debe implementar el ISoapService.SaveTodoItemAsync
método. Este método detecta si TodoItem
es un elemento nuevo o actualizado y llama al método adecuado en el todoService
objeto. Los controladores de eventos CreateTodoItemCompleted
y EditTodoItemCompleted
también deben implementarse para que sepa cuándo el todoService
ha recibido una respuesta del servicio ASMX (estos se pueden combinar en un único controlador porque realizan la misma operación). En el ejemplo siguiente se muestran las implementaciones de interfaz y controlador de eventos, así como el objeto TaskCompletionSource
usado para operar de forma asincrónica:
public class SoapService : ISoapService
{
TaskCompletionSource<bool> saveRequestComplete = null;
...
public SoapService()
{
...
todoService.CreateTodoItemCompleted += TodoService_SaveTodoItemCompleted;
todoService.EditTodoItemCompleted += TodoService_SaveTodoItemCompleted;
}
public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
try
{
var todoItem = ToASMXServiceTodoItem(item);
saveRequestComplete = new TaskCompletionSource<bool>();
if (isNewItem)
{
todoService.CreateTodoItemAsync(todoItem);
}
else
{
todoService.EditTodoItemAsync(todoItem);
}
await saveRequestComplete.Task;
}
catch (SoapException se)
{
Debug.WriteLine("\t\t{0}", se.Message);
}
catch (Exception ex)
{
Debug.WriteLine("\t\tERROR {0}", ex.Message);
}
}
private void TodoService_SaveTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
saveRequestComplete?.TrySetResult(true);
}
...
}
Eliminación de datos
La eliminación de datos requiere una implementación similar. Defina un TaskCompletionSource
, implemente un controlador de eventos y el método ISoapService.DeleteTodoItemAsync
:
public class SoapService : ISoapService
{
TaskCompletionSource<bool> deleteRequestComplete = null;
...
public SoapService()
{
...
todoService.DeleteTodoItemCompleted += TodoService_DeleteTodoItemCompleted;
}
public async Task DeleteTodoItemAsync (string id)
{
try
{
deleteRequestComplete = new TaskCompletionSource<bool>();
todoService.DeleteTodoItemAsync(id);
await deleteRequestComplete.Task;
}
catch (SoapException se)
{
Debug.WriteLine("\t\t{0}", se.Message);
}
catch (Exception ex)
{
Debug.WriteLine("\t\tERROR {0}", ex.Message);
}
}
private void TodoService_DeleteTodoItemCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
deleteRequestComplete?.TrySetResult(true);
}
...
}
Prueba del servicio web
La prueba de dispositivos físicos o emulados con un servicio hospedado localmente requiere la configuración personalizada de IIS, las direcciones de punto de conexión y las reglas de firewall. Para obtener más información sobre cómo configurar el entorno para las pruebas, vea la Configuración del acceso remoto a IIS Express. La única diferencia entre probar WCF y ASMX es el número de puerto de TodoService.