Consumo de un servicio web ASP.NET (ASMX)

Descargar ejemploDescargar el ejemplo

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:

Aplicación de ejemplo

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:

  1. Crea instancias de TodoService como una instancia de nivel de clase.
  2. Crea una colección denominada Items para almacenar TodoItem objetos.
  3. 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 TaskCompletionSourceobjeto , 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 TaskCompletionSourcecontrolador 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.