Consumo de un servicio web ASP.NET (ASMX)
ASMX proporciona la capacidad de crear servicios web que envían mensajes mediante el Protocolo simple de acceso a objetos (SOAP). SOAP es un protocolo independiente de la plataforma e independiente del lenguaje para compilar y acceder a servicios web. 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 comprender cómo enviar y recibir mensajes SOAP. En este artículo se muestra cómo consumir un servicio SOAP ASMX desde una Xamarin.Forms aplicación.
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.
- Elemento Header opcional 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, lo que 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 recursos de Internet (como el servidor back-end de la aplicación) y la aplicación, lo que evita 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, se producirá un error con una excepción.
ATS puede optar por no participar si no es posible usar el HTTPS
protocolo 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 App Transport Security.
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, consulte 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 del lenguaje de descripción de servicios web (WSDL) del servicio.
Por ejemplo, la GetTodoItems
acción de servicio da como resultado un GetTodoItemsAsync
método y un GetTodoItemsCompleted
evento 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 GetTodoItemsCompleted
evento y proporciona los datos de respuesta dentro de la propiedad del Result
evento.
Creación de la implementación de ISoapService
Para permitir que el proyecto multiplataforma compartido 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 de
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 .TodoService
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 TodoItem
clase para modelar datos. Para almacenar un TodoItem
elemento en el servicio web, primero debe convertirse al tipo generado por TodoItem
proxy. Esto se logra mediante el ToASMXServiceTodoItem
método , 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.
Del mismo modo, cuando se recuperan datos del servicio web, se debe convertir del tipo generado por TodoItem
proxy a una TodoItem
instancia de . Esto se logra con el FromASMXServiceTodoItem
método , 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 los establece en la instancia recién creada TodoItem
.
Recuperación de datos
La ISoapService
interfaz espera que el RefreshDataAsync
método devuelva un Task
elemento con la colección item. Sin embargo, el TodoService.GetTodoItemsAsync
método 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
objeto , se inicia la llamada asincrónica en el RefreshDataAsync
método y se espera el Task
proporcionado por .TaskCompletionSource
Cuando se invoca el TodoService_GetTodoItemsCompleted
controlador de eventos, rellena la Items
colección y actualiza :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 CreateTodoItemCompleted
controladores de eventos y EditTodoItemCompleted
también deben implementarse para que sepa cuándo 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 TaskCompletionSource
objeto utilizado 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 , implemente un TaskCompletionSource
controlador de eventos y el ISoapService.DeleteTodoItemAsync
método :
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 que haya reglas de firewall, direcciones de punto de conexión y configuración de IIS personalizadas. Para obtener más información sobre cómo configurar el entorno para las pruebas, consulte configuración del acceso remoto a IIS Express. La única diferencia entre probar WCF y ASMX es el número de puerto de TodoService.