Consumir un servicio web de Windows Communication Foundation (WCF)
WCF es el marco unificado de Microsoft para crear aplicaciones orientadas a servicios. Permite a los desarrolladores crear aplicaciones distribuidas seguras, confiables, con transacciones 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: definen las estructuras de datos que forman la base del contenido dentro de un mensaje.
- Contratos de mensajes: redactan mensajes a partir de contratos de datos existentes.
- Contratos de error: permiten especificar errores SOAP personalizados.
- Contratos de servicio: especifican 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 el consumo de un servicio ASMX, consulte Consumo de servicios web (ASMX)de ASP.NET.
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 clase BasicHttpBinding
.
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 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:
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 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 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.
Se debe generar un proxy para consumir un servicio WCF, lo que permite a la aplicación conectarse al servicio. El proxy se construye mediante el consumo de metadatos de servicio que define 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 WCF Web Service Reference de Microsoft en Visual Studio 2017 para agregar una referencia de servicio para el servicio web a una biblioteca estándar de .NET. Una alternativa a la creación del proxy mediante el WCF Web Service Reference de Microsoft en Visual Studio 2017 es usar la herramienta de utilidad de metadatos ServiceModel (svcutil.exe). Para obtener más información, consulte Herramienta de utilidad de metadatos de ServiceModel (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 se implementa una operación asincrónica como dos métodos denominados BeginOperationName y EndOperationName, que comienzan y terminan la operación asincrónica.
El método BeginOperationName inicia la operación asincrónica y devuelve un objeto que implementa la interfaz IAsyncResult
. Después de llamar a BeginOperationName, una aplicación puede seguir ejecutando instrucciones en el subproceso de llamada, mientras que la operación asincrónica tiene lugar en un subproceso de 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 parámetro IAsyncResult
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 encapsulando las operaciones asincrónicas en el mismo objeto Task
. Esta encapsulación se proporciona mediante varias sobrecargas del método TaskFactory.FromAsync
.
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 clase TodoServiceClient
, 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 Introducción a la compatibilidad asincrónica.
La instancia TodoServiceClient
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 instancia TodoServiceClient
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 referenciade servicio.
Crear 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 ToWCFServiceTodoItem
, 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 instancia TodoWCFService.TodoItem
y establece cada propiedad en la propiedad idéntica de la instancia TodoItem
.
De forma similar, cuando los datos se recuperan del servicio web, se deben convertir del tipo generado TodoItem
por proxy a una instancia TodoItem
. Esto se consigue con el método FromWCFServiceTodoItem
, 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 métodos TodoServiceClient.BeginGetTodoItems
y TodoServiceClient.EndGetTodoItems
se usan para llamar a la operación GetTodoItems
proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un objeto Task
, 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 método TodoServiceClient.EndGetTodoItems
una vez completado el método TodoServiceClient.BeginGetTodoItems
, con el parámetro null
que indica que no se pasa ningún dato al delegado BeginGetTodoItems
. Por último, el valor de la enumeración TaskCreationOptions
especifica que se debe usar el comportamiento predeterminado para la creación y ejecución de tareas.
El método TodoServiceClient.EndGetTodoItems
devuelve una ObservableCollection
de TodoWCFService.TodoItem
instancias, que luego se convierte en una List
de TodoItem
instancias para mostrar.
Crear datos
Los métodos TodoServiceClient.BeginCreateTodoItem
y TodoServiceClient.EndCreateTodoItem
se usan para llamar a la operación CreateTodoItem
proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un objeto Task
, 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 objeto Task
que ejecuta el método TodoServiceClient.EndCreateTodoItem
una vez completado el método TodoServiceClient.BeginCreateTodoItem
, con el parámetro todoItem
siendo los datos que se pasan al BeginCreateTodoItem
delegado para especificar el objeto TodoItem
que va a crear el servicio web. Por último, el valor de la enumeración TaskCreationOptions
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 el TodoItem
, que se controla mediante la aplicación.
Actualizar datos
Los métodos TodoServiceClient.BeginEditTodoItem
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 objeto Task
, 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 método Task.Factory.FromAsync
crea un objeto Task
que ejecuta el método TodoServiceClient.EndEditTodoItem
una vez completado el método TodoServiceClient.BeginCreateTodoItem
, con el parámetro todoItem
siendo los datos que se pasan al delegado BeginEditTodoItem
para especificar el objeto TodoItem
que va a actualizar el servicio web. Por último, el valor de la enumeración TaskCreationOptions
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 TodoItem
, que se controla mediante la aplicación.
Eliminación de datos
Los métodos TodoServiceClient.BeginDeleteTodoItem
y TodoServiceClient.EndDeleteTodoItem
se usan para llamar a la operación DeleteTodoItem
proporcionada por el servicio web. Estos métodos asincrónicos se encapsulan en un objeto Task
, 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 método Task.Factory.FromAsync
crea un objeto Task
que ejecuta el método TodoServiceClient.EndDeleteTodoItem
una vez completado el método TodoServiceClient.BeginDeleteTodoItem
, con el parámetro id
siendo los datos que se pasan al delegado BeginDeleteTodoItem
para especificar el objeto TodoItem
que va a eliminar el servicio web. Por último, el valor de la enumeración TaskCreationOptions
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 el TodoItem
que 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 IP 192.168.1.143
. En los pasos siguientes se explica cómo configurar Windows 10 e IIS Express para aceptar conexiones remotas y conectarse al servicio desde un dispositivo físico o virtual:
Agregue una excepción a 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 un puerto de apertura de regla de entrada 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
Configure IIS Express para aceptar conexiones remotas. Puede configurar IIS Express editando el archivo de configuración de IIS Express en [directorio de soluciones].vs\config\applicationhost.config. Busque el elemento
site
con el nombreTodoWCFService
. Debería 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 elementos
binding
para abrir el puerto 49393 al tráfico exterior y al emulador de Android. El enlace usa un formato[IP address]:[port]:[hostname]
que especifica cómo IIS Express responderá a las solicitudes. Las solicitudes externas tendrán nombres de host que se deben especificar comobinding
. Agregue el siguiente XML al elementobindings
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 elemento debe tener un aspecto similar al
bindings
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á 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. Esto 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 ser incorrecto en la configuración de IIS Express (la solicitud llega a IIS Express pero se rechaza). Si recibe un error diferente, puede ser que la aplicación no se esté ejecutando o que el firewall esté configurado incorrectamente.Para permitir que IIS Express siga ejecutándose y sirviendo el servicio, desactive la opción Editar y continuar en Propiedades del proyecto>>Depuradores web.
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.
El emulador de Android utiliza un proxy interno que impide que el emulador acceda directamente a la dirección
localhost
de la máquina host. En su lugar, la dirección10.0.2.2
del emulador se enruta alocalhost
en el equipo host a través de un proxy interno. Estas solicitudes proxy tendrán127.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
192.168.1.143
, pero es probable que la 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) tiene 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 configurado el 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.