Consumo de un servicio web de Windows Communication Foundation (WCF)

Descargar ejemploDescargar el ejemplo

WCF es el marco unificado de Microsoft para compilar aplicaciones orientadas a servicios. Permite a los desarrolladores crear aplicaciones distribuidas seguras, confiables, transaccionadas e interoperables. En este artículo se muestra cómo consumir un servicio WCF Simple Object Access Protocol (SOAP) desde una Xamarin.Forms aplicación.

WCF describe un servicio con una variedad de contratos diferentes, entre los que se incluyen:

  • Contratos de datos : defina las estructuras de datos que forman la base del contenido dentro de un mensaje.
  • Contratos de mensajes: componen mensajes de contratos de datos existentes.
  • Contratos de error : permite especificar errores SOAP personalizados.
  • Contratos de servicio : especifique las operaciones que admiten los servicios y los mensajes necesarios para interactuar con cada operación. También especifican cualquier comportamiento de error personalizado que se pueda asociar a las operaciones en cada servicio.

Hay diferencias entre ASP.NET servicios web (ASMX) y WCF, pero WCF admite las mismas funcionalidades que PROPORCIONA ASMX: mensajes SOAP a través de HTTP. Para obtener más información sobre cómo consumir un servicio ASMX, vea Consumir ASP.NET servicios web (ASMX).

Importante

La compatibilidad de la plataforma Xamarin con WCF se limita a los mensajes SOAP codificados con texto a través de HTTP/HTTPS mediante la BasicHttpBinding clase .

La compatibilidad con WCF requiere el uso de herramientas solo disponibles en un entorno de Windows para generar el proxy y hospedar TodoWCFService. La compilación y prueba de la aplicación de iOS requerirá la implementación de TodoWCFService en un equipo Windows o como servicio web de Azure.

Normalmente, las aplicaciones nativas de Xamarin Forms comparten código con una biblioteca de clases estándar de .NET. Sin embargo, .NET Core no admite ACTUALMENTE WCF, por lo que el proyecto compartido debe ser una biblioteca de clases portable heredada. Para obtener información sobre la compatibilidad con WCF en .NET Core, consulte Elección entre .NET Core y .NET Framework para aplicaciones de servidor.

La solución de aplicación de ejemplo incluye un servicio WCF que se puede ejecutar localmente y 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 los 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, producirán 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 WCF 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 todoItem serializado XML
EditTodoItem Actualizar una tarea pendiente Un todoItem serializado XML
DeleteTodoItem Eliminar una tarea pendiente Un todoItem serializado XML

Para obtener más información sobre el modelo de datos usado en la aplicación, consulte Modelado de los datos.

Se debe generar un proxy para consumir un servicio WCF, que permite a la aplicación conectarse al servicio. El proxy se construye mediante el consumo de metadatos de servicio que definen los métodos y la configuración del servicio asociada. Estos metadatos se exponen en forma de un documento de lenguaje de descripción de servicios web (WSDL) generado por el servicio web. El proxy se puede compilar mediante el proveedor de Microsoft WCF Web Service Reference en Visual Studio 2017 para agregar una referencia de servicio para el servicio web a una biblioteca de .NET Standard. Una alternativa a la creación del proxy mediante el proveedor de Microsoft WCF Web Service Reference en Visual Studio 2017 es usar la Herramienta de utilidad de metadatos de ServiceModel (svcutil.exe). Para obtener más información, vea ServiceModel Metadata Utility Tool (Svcutil.exe).

Las clases de proxy generadas proporcionan métodos para consumir los servicios web que usan el patrón de diseño Modelo de programación asincrónica (APM). En este patrón, una operación asincrónica se implementa como dos métodos denominados BeginOperationName y EndOperationName, que comienzan y finalizan la operación asincrónica.

El método BeginOperationName inicia la operación asincrónica y devuelve un objeto que implementa la IAsyncResult interfaz. Después de llamar a BeginOperationName, una aplicación puede seguir ejecutando instrucciones en el subproceso que realiza la llamada, mientras que la operación asincrónica tiene lugar en un subproceso del grupo de subprocesos.

Para cada llamada a BeginOperationName, la aplicación también debe llamar a EndOperationName para obtener los resultados de la operación. El valor devuelto de EndOperationName es el mismo tipo devuelto por el método de servicio web sincrónico. Por ejemplo, el EndGetTodoItems método devuelve una colección de TodoItem instancias. El método EndOperationName también incluye un IAsyncResult parámetro que se debe establecer en la instancia devuelta por la llamada correspondiente al método BeginOperationName .

La Biblioteca paralela de tareas (TPL) puede simplificar el proceso de consumo de un par de métodos de inicio y fin de APM mediante la encapsulación de las operaciones asincrónicas en el mismo Task objeto. Esta encapsulación se proporciona mediante varias sobrecargas del TaskFactory.FromAsync método .

Para obtener más información sobre APM, vea Modelo de programación asincrónica y TPL y Programación asincrónica tradicional de .NET Framework en MSDN.

Creación del objeto TodoServiceClient

La clase de proxy generada proporciona la TodoServiceClient clase , que se usa para comunicarse con el servicio WCF a través de HTTP. Proporciona funcionalidad para invocar métodos de servicio web como operaciones asincrónicas desde una instancia de servicio identificada por URI. Para obtener más información sobre las operaciones asincrónicas, consulte Información general sobre la compatibilidad asincrónica.

La TodoServiceClient instancia se declara en el nivel de clase para que el objeto resida mientras la aplicación necesite consumir el servicio WCF, como se muestra en el ejemplo de código siguiente:

public class SoapService : ISoapService
{
  ITodoService todoService;
  ...

  public SoapService ()
  {
    todoService = new TodoServiceClient (
      new BasicHttpBinding (),
      new EndpointAddress (Constants.SoapUrl));
  }
  ...
}

La TodoServiceClient instancia se configura con información de enlace y una dirección de punto de conexión. Un enlace se usa para especificar los detalles de transporte, codificación y protocolo necesarios para que las aplicaciones y los servicios se comuniquen entre sí. BasicHttpBinding Especifica que los mensajes SOAP codificados con texto se enviarán a través del protocolo de transporte HTTP. Especificar una dirección de punto de conexión permite a la aplicación conectarse a diferentes instancias del servicio WCF, siempre que haya varias instancias publicadas.

Para obtener más información sobre cómo configurar la referencia de servicio, consulte Configuración de la referencia de servicio.

Creación de objetos de transferencia de datos

La aplicación de ejemplo usa la TodoItem clase para modelar los datos. Para almacenar un TodoItem elemento en el servicio web, primero debe convertirse al tipo generado por TodoItem proxy. Esto se logra mediante el ToWCFServiceTodoItem método , como se muestra en el ejemplo de código siguiente:

TodoWCFService.TodoItem ToWCFServiceTodoItem (TodoItem item)
{
  return new TodoWCFService.TodoItem
  {
    ID = item.ID,
    Name = item.Name,
    Notes = item.Notes,
    Done = item.Done
  };
}

Este método simplemente crea una nueva TodoWCFService.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 FromWCFServiceTodoItem método , como se muestra en el ejemplo de código siguiente:

static TodoItem FromWCFServiceTodoItem (TodoWCFService.TodoItem item)
{
  return new TodoItem
  {
    ID = item.ID,
    Name = item.Name,
    Notes = item.Notes,
    Done = item.Done
  };
}

Este método simplemente recupera los datos del tipo generado por TodoItem proxy y lo establece en la instancia recién creada TodoItem .

Recuperación de datos

Los TodoServiceClient.BeginGetTodoItems métodos y TodoServiceClient.EndGetTodoItems se usan para llamar a la GetTodoItems operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un Task objeto, como se muestra en el ejemplo de código siguiente:

public async Task<List<TodoItem>> RefreshDataAsync ()
{
  ...
  var todoItems = await Task.Factory.FromAsync <ObservableCollection<TodoWCFService.TodoItem>> (
    todoService.BeginGetTodoItems,
    todoService.EndGetTodoItems,
    null,
    TaskCreationOptions.None);

  foreach (var item in todoItems)
  {
    Items.Add (FromWCFServiceTodoItem (item));
  }
  ...
}

El Task.Factory.FromAsync método crea un Task objeto que ejecuta el TodoServiceClient.EndGetTodoItems método una vez completado el TodoServiceClient.BeginGetTodoItems método, con el null parámetro que indica que no se pasa ningún dato al BeginGetTodoItems delegado. Por último, el valor de la TaskCreationOptions enumeración especifica que se debe usar el comportamiento predeterminado para la creación y ejecución de tareas.

El TodoServiceClient.EndGetTodoItems método devuelve una ObservableCollection de TodoWCFService.TodoItem instancias, que luego se convierte en una List de TodoItem instancias para mostrar.

Creación de datos

Los TodoServiceClient.BeginCreateTodoItem métodos y TodoServiceClient.EndCreateTodoItem se usan para llamar a la CreateTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un Task objeto, como se muestra en el ejemplo de código siguiente:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
  ...
  var todoItem = ToWCFServiceTodoItem (item);
  ...
  await Task.Factory.FromAsync (
    todoService.BeginCreateTodoItem,
    todoService.EndCreateTodoItem,
    todoItem,
    TaskCreationOptions.None);
  ...
}

El Task.Factory.FromAsync método crea un Task objeto que ejecuta el TodoServiceClient.EndCreateTodoItem método una vez completado el TodoServiceClient.BeginCreateTodoItem método, siendo el todoItem parámetro los datos que se pasan al BeginCreateTodoItem delegado para especificar el TodoItem objeto que va a crear el servicio web. Por último, el valor de la TaskCreationOptions enumeración especifica que se debe usar el comportamiento predeterminado para la creación y ejecución de tareas.

El servicio web produce un FaultException si no puede crear , TodoItemque se controla mediante la aplicación.

Actualización de datos

Los TodoServiceClient.BeginEditTodoItem métodos y TodoServiceClient.EndEditTodoItem se usan para llamar a la EditTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un Task objeto, como se muestra en el ejemplo de código siguiente:

public async Task SaveTodoItemAsync (TodoItem item, bool isNewItem = false)
{
  ...
  var todoItem = ToWCFServiceTodoItem (item);
  ...
  await Task.Factory.FromAsync (
    todoService.BeginEditTodoItem,
    todoService.EndEditTodoItem,
    todoItem,
    TaskCreationOptions.None);
  ...
}

El Task.Factory.FromAsync método crea un Task objeto que ejecuta el TodoServiceClient.EndEditTodoItem método una vez completado el TodoServiceClient.BeginCreateTodoItem método, siendo el todoItem parámetro los datos que se pasan al BeginEditTodoItem delegado para especificar el TodoItem objeto que va a actualizar el servicio web. Por último, el valor de la TaskCreationOptions enumeración especifica que se debe usar el comportamiento predeterminado para la creación y ejecución de tareas.

El servicio web produce un FaultException si no encuentra o actualiza , TodoItemque la aplicación controla.

Eliminación de datos

Los TodoServiceClient.BeginDeleteTodoItem métodos y TodoServiceClient.EndDeleteTodoItem se usan para llamar a la DeleteTodoItem operación proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un Task objeto , como se muestra en el ejemplo de código siguiente:

public async Task DeleteTodoItemAsync (string id)
{
  ...
  await Task.Factory.FromAsync (
    todoService.BeginDeleteTodoItem,
    todoService.EndDeleteTodoItem,
    id,
    TaskCreationOptions.None);
  ...
}

El Task.Factory.FromAsync método crea un Task objeto que ejecuta el TodoServiceClient.EndDeleteTodoItem método una vez completado el TodoServiceClient.BeginDeleteTodoItem método, siendo el id parámetro los datos que se pasan al BeginDeleteTodoItem delegado para especificar que TodoItem el servicio web va a eliminar. Por último, el valor de la TaskCreationOptions enumeración especifica que se debe usar el comportamiento predeterminado para la creación y ejecución de tareas.

El servicio web produce un FaultException si no encuentra o elimina , TodoItemque la aplicación controla.

Configuración del acceso remoto a IIS Express

En Visual Studio 2017 o Visual Studio 2019, deberías poder probar la aplicación para UWP en un equipo sin ninguna configuración adicional. La prueba de clientes Android e iOS puede requerir los pasos adicionales de esta sección. Consulte Conexión a servicios web locales desde simuladores de iOS y emuladores de Android para obtener más información.

De forma predeterminada, IIS Express solo responderá a las solicitudes a localhost. Los dispositivos remotos (como un dispositivo Android, un iPhone o incluso un simulador) no tendrán acceso al servicio WCF local. Necesitará conocer la dirección IP de la estación de trabajo de Windows 10 en la red local. Para este ejemplo, supongamos que la estación de trabajo tiene la dirección 192.168.1.143IP . En los pasos siguientes se explica cómo configurar Windows 10 y IIS Express para aceptar conexiones remotas y conectarse al servicio desde un dispositivo físico o virtual:

  1. Agregue una excepción al Firewall de Windows. Debe abrir un puerto a través del Firewall de Windows que las aplicaciones de la subred pueden usar para comunicarse con el servicio WCF. Cree una regla de entrada que abra el puerto 49393 en el firewall. Desde un símbolo del sistema administrativo, ejecute este comando:

    netsh advfirewall firewall add rule name="TodoWCFService" dir=in protocol=tcp localport=49393 profile=private remoteip=localsubnet action=allow
    
  2. Configure IIS Express para aceptar conexiones remotas. Puede configurar IIS Express editando el archivo de configuración para IIS Express en [directorio de soluciones].vs\config\applicationhost.config. Busque el site elemento con el nombre TodoWCFService. Debe tener un aspecto similar al siguiente XML:

    <site name="TodoWCFService" id="2">
        <application path="/" applicationPool="Clr4IntegratedAppPool">
            <virtualDirectory path="/" physicalPath="C:\Users\tom\TodoWCF\TodoWCFService\TodoWCFService" />
        </application>
        <bindings>
            <binding protocol="http" bindingInformation="*:49393:localhost" />
        </bindings>
    </site>
    

    Deberá agregar dos binding elementos para abrir el puerto 49393 al tráfico exterior y al emulador de Android. El enlace usa un [IP address]:[port]:[hostname] formato que especifica cómo IIS Express responderá a las solicitudes. Las solicitudes externas tendrán nombres de host que se deben especificar como .binding Agregue el siguiente XML al bindings elemento y reemplace la dirección IP por su propia dirección IP:

    <binding protocol="http" bindingInformation="*:49393:192.168.1.143" />
    <binding protocol="http" bindingInformation="*:49393:127.0.0.1" />
    

    Después de cambiar el bindings elemento debe tener un aspecto similar al siguiente:

    <site name="TodoWCFService" id="2">
        <application path="/" applicationPool="Clr4IntegratedAppPool">
            <virtualDirectory path="/" physicalPath="C:\Users\tom\TodoWCF\TodoWCFService\TodoWCFService" />
        </application>
        <bindings>
            <binding protocol="http" bindingInformation="*:49393:localhost" />
            <binding protocol="http" bindingInformation="*:49393:192.168.1.143" />
            <binding protocol="http" bindingInformation="*:49393:127.0.0.1" />
        </bindings>
    </site>
    

    Importante

    De forma predeterminada, IIS Express no aceptarán conexiones de orígenes externos por motivos de seguridad. Para habilitar conexiones desde dispositivos remotos, debe ejecutar IIS Express con permisos administrativos. La manera más fácil de hacerlo es ejecutar Visual Studio 2017 con permisos administrativos. Se iniciará IIS Express con permisos administrativos al ejecutar TodoWCFService.

    Con estos pasos completados, debería poder ejecutar TodoWCFService y conectarse desde otros dispositivos de la subred. Para probarlo, ejecute la aplicación y visite http://localhost:49393/TodoService.svc. Si recibe un error de solicitud incorrecta al visitar esa dirección URL, bindings puede que sea incorrecto en la configuración de IIS Express (la solicitud llega a IIS Express pero se rechaza). Si recibe un error diferente, es posible que la aplicación no se esté ejecutando o que el firewall esté configurado incorrectamente.

    Para permitir que IIS Express siga ejecutándose y atendiendo al servicio, desactive la opción Editar y continuar en Depuradores web > de propiedades > del proyecto.

  3. Personalice los dispositivos de punto de conexión que usan para acceder al servicio. Este paso implica configurar la aplicación cliente, que se ejecuta en un dispositivo físico o emulado, para acceder al servicio WCF.

    Android Emulator utiliza un proxy interno que impide que el emulador acceda directamente a la dirección de localhost la máquina host. En su lugar, la dirección 10.0.2.2 del emulador se enruta a localhost en el equipo host a través de un proxy interno. Estas solicitudes con proxy tendrán 127.0.0.1 como nombre de host en el encabezado de solicitud, por lo que creó el enlace de IIS Express para este nombre de host en los pasos anteriores.

    El simulador de iOS se ejecuta en un host de compilación de Mac, incluso si usa el simulador remoto de iOS para Windows. Las solicitudes de red del simulador tendrán la dirección IP de la estación de trabajo en la red local como nombre de host (en este ejemplo, es , pero es probable que 192.168.1.143la dirección IP real sea diferente). Este es el motivo por el que creó el enlace de IIS Express para este nombre de host en los pasos anteriores.

    Asegúrese de que la SoapUrl propiedad del archivo Constants.cs del proyecto TodoWCF (Portable) tenga valores correctos para la red:

    public static string SoapUrl
    {
        get
        {
            var defaultUrl = "http://localhost:49393/TodoService.svc";
    
            if (Device.RuntimePlatform == Device.Android)
            {
                defaultUrl = "http://10.0.2.2:49393/TodoService.svc";
            }
            else if (Device.RuntimePlatform == Device.iOS)
            {
                defaultUrl = "http://192.168.1.143:49393/TodoService.svc";
            }
    
            return defaultUrl;
        }
    }
    

    Una vez que haya configurado Constants.cs con los puntos de conexión adecuados, debería poder conectarse a TodoWCFService que se ejecuta en la estación de trabajo de Windows 10 desde dispositivos físicos o virtuales.