Consumo de un servicio web de Windows Communication Foundation (WCF)
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:
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 , TodoItem
que 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 , TodoItem
que 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 , 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 192.168.1.143
IP . 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:
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
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 nombreTodoWCFService
. 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 albindings
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.
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ón10.0.2.2
del emulador se enruta alocalhost
en el equipo host a través de un proxy interno. Estas solicitudes con 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 , pero es probable que
192.168.1.143
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) 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.