Introducción a los servicios web

En esta guía se muestra cómo consumir diferentes tecnologías de servicios web. Los temas tratados incluyen la comunicación con los servicios REST, los servicios SOAP y los servicios de Windows Communication Foundation.

Para funcionar correctamente, muchas aplicaciones móviles dependen de la nube, por lo que la integración de servicios web en aplicaciones móviles es un escenario común. La plataforma Xamarin admite el consumo de diferentes tecnologías de servicios web e incluye compatibilidad integrada y de terceros para consumir servicios RESTful, ASMX y Windows Communication Foundation (WCF).

Para los clientes que usan Xamarin.Forms, hay ejemplos completos que usan cada una de estas tecnologías en la documentación de servicios web de Xamarin.Forms .

Importante

En iOS 9, 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.

Puede optar por no participar en ATS 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.

REST

Transferencia de estado representacional (REST) es un estilo arquitectónico para compilar servicios web. Las solicitudes REST se realizan a través de HTTP con los mismos verbos HTTP que usan los exploradores web para recuperar páginas web y enviar datos a los servidores. Los verbos son:

  • GET: esta operación se usa para recuperar datos del servicio web.
  • POST: esta operación se usa para crear un nuevo elemento de datos en el servicio web.
  • PUT: esta operación se usa para actualizar un elemento de datos en el servicio web.
  • PATCH: esta operación se usa para actualizar un elemento de datos en el servicio web describiendo un conjunto de instrucciones sobre cómo se debe modificar el elemento. Este verbo no se usa en la aplicación de ejemplo.
  • DELETE: esta operación se usa para eliminar un elemento de datos en el servicio web.

Las API de servicio web que se adhieren a REST se denominan API de RESTful y se definen mediante:

  • Identificador URI base.
  • Métodos HTTP, como GET, POST, PUT, PATCH o DELETE.
  • Tipo de medio para los datos, como notación de objetos JavaScript (JSON).

La simplicidad de REST ha ayudado a convertirla en el método principal para acceder a los servicios web en aplicaciones móviles.

Consumo de servicios REST

Hay una serie de bibliotecas y clases que se pueden usar para consumir servicios REST y las siguientes subsecciones las describen. Para obtener más información sobre el consumo de un servicio REST, consulte Consumo de un servicio web RESTful.

HttpClient

Las bibliotecas de cliente HTTP de Microsoft proporcionan la HttpClient clase , que se usa para enviar y recibir solicitudes a través de HTTP. Proporciona funcionalidad para enviar solicitudes HTTP y recibir respuestas HTTP de un recurso identificado por URI. Cada solicitud se envía como una operación asincrónica. Para obtener más información sobre las operaciones asincrónicas, vea Async Support Overview.

La HttpResponseMessage clase representa un mensaje de respuesta HTTP recibido del servicio web después de realizar una solicitud HTTP. Contiene información sobre la respuesta, incluido el código de estado, los encabezados y el cuerpo. La HttpContent clase representa el cuerpo HTTP y los encabezados de contenido, como Content-Type y Content-Encoding. El contenido se puede leer mediante cualquiera de los ReadAs métodos, como ReadAsStringAsync y ReadAsByteArrayAsync, en función del formato de los datos.

Para obtener más información sobre la HttpClient clase , vea Creating the HTTPClient Object.

HTTPWebRequest

Llamar a servicios web con HTTPWebRequest implica:

  • Creación de la instancia de solicitud para un URI determinado.
  • Establecer varias propiedades HTTP en la instancia de solicitud.
  • Recuperar un objeto HttpWebResponse de la solicitud.
  • Leer datos de la respuesta.

Por ejemplo, el código siguiente recupera datos del servicio web de la Biblioteca Nacional de Medicina de ee. UU.:

var rxcui = "198440";
var request = HttpWebRequest.Create(string.Format(@"https://rxnav.nlm.nih.gov/REST/RxTerms/rxcui/{0}/allinfo", rxcui));
request.ContentType = "application/json";
request.Method = "GET";

using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
  if (response.StatusCode != HttpStatusCode.OK)
     Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
  using (StreamReader reader = new StreamReader(response.GetResponseStream()))
  {
               var content = reader.ReadToEnd();
               if(string.IsNullOrWhiteSpace(content)) {
                       Console.Out.WriteLine("Response contained empty body...");
               }
               else {
                       Console.Out.WriteLine("Response Body: \r\n {0}", content);
               }

               Assert.NotNull(content);
  }
}

En el ejemplo anterior se crea un HttpWebRequest objeto que devolverá datos con formato JSON. Los datos se devuelven en un HttpWebResponseobjeto , desde el que se puede obtener un StreamReader para leer los datos.

RestSharp

Otro enfoque para consumir servicios REST es usar la biblioteca RestSharp . RestSharp encapsula las solicitudes HTTP, incluida la compatibilidad para recuperar resultados como contenido de cadena sin formato o como un objeto C# deserializado. Por ejemplo, el código siguiente realiza una solicitud al servicio web de la Biblioteca Nacional de Medicina de ee. UU. y recupera los resultados como una cadena con formato JSON:

var request = new RestRequest(string.Format("{0}/allinfo", rxcui));
request.RequestFormat = DataFormat.Json;
var response = Client.Execute(request);
if(string.IsNullOrWhiteSpace(response.Content) || response.StatusCode != System.Net.HttpStatusCode.OK) {
       return null;
}
rxTerm = DeserializeRxTerm(response.Content);

DeserializeRxTerm es un método que tomará la cadena JSON sin formato de la RestSharp.RestResponse.Content propiedad y la convertirá en un objeto de C#. La deserialización de los datos devueltos desde servicios web se describe más adelante en este artículo.

NSUrlConnection

Además de las clases disponibles en la biblioteca de clases base (BCL) mono, como HttpWebRequestlas bibliotecas de C# de terceros, como RestSharp, las clases específicas de la plataforma también están disponibles para consumir servicios web. Por ejemplo, en iOS, se pueden usar las NSUrlConnection clases y NSMutableUrlRequest .

En el ejemplo de código siguiente se muestra cómo llamar al servicio web de la Biblioteca Nacional de Medicina de EE. UU. mediante clases de iOS:

var rxcui = "198440";
var request = new NSMutableUrlRequest(new NSUrl(string.Format("https://rxnav.nlm.nih.gov/REST/RxTerms/rxcui/{0}/allinfo", rxcui)),
       NSUrlRequestCachePolicy.ReloadRevalidatingCacheData, 20);
request["Accept"] = "application/json";

var connectionDelegate = new RxTermNSURLConnectionDelegate();
var connection = new NSUrlConnection(request, connectionDelegate);
connection.Start();

public class RxTermNSURLConnectionDelegate : NSUrlConnectionDelegate
{
       StringBuilder _ResponseBuilder;
       public bool IsFinishedLoading { get; set; }
       public string ResponseContent { get; set; }

       public RxTermNSURLConnectionDelegate()
               : base()
       {
               _ResponseBuilder = new StringBuilder();
       }

       public override void ReceivedData(NSUrlConnection connection, NSData data)
       {
               if(data != null) {
                       _ResponseBuilder.Append(data.ToString());
               }
       }
       public override void FinishedLoading(NSUrlConnection connection)
       {
               IsFinishedLoading = true;
               ResponseContent = _ResponseBuilder.ToString();
       }
}

Por lo general, las clases específicas de la plataforma para consumir servicios web deben limitarse a escenarios en los que el código nativo se traslada a C#. Siempre que sea posible, el código de acceso al servicio web debe ser portátil para que se pueda compartir entre plataformas.

ServiceStack

Otra opción para llamar a servicios web es la biblioteca de Service Stack . Por ejemplo, el código siguiente muestra cómo usar el método de IServiceClient.GetAsync Service Stack para emitir una solicitud de servicio:

client.GetAsync<CustomersResponse>("",
          (response) => {
               foreach(var c in response.Customers) {
                       Console.WriteLine(c.CompanyName);
               }
       },
       (response, ex) => {
               Console.WriteLine(ex.Message);
       });

Importante

Aunque las herramientas como ServiceStack y RestSharp facilitan la llamada y el consumo de servicios REST, a veces no es trivial consumir XML o JSON que no se ajusta a las convenciones de serialización estándar de DataContract . Si es necesario, invoque la solicitud y controle la serialización adecuada explícitamente mediante la biblioteca ServiceStack.Text que se describe a continuación.

Consumo de datos RESTful

Normalmente, los servicios web RESTful usan mensajes JSON para devolver datos al cliente. JSON es un formato de intercambio de datos basado en texto que genera cargas compactas, lo que da lugar a requisitos de ancho de banda reducidos al enviar datos. En esta sección, se examinarán los mecanismos para consumir respuestas RESTful en JSON y Plain-Old-XML (POX).

System.JSON

La plataforma de Xamarin se incluye con compatibilidad con JSON de fábrica. Mediante un JsonObject, los resultados se pueden recuperar como se muestra en el ejemplo de código siguiente:

var obj = JsonObject.Parse(json);
var properties = obj["rxtermsProperties"];
term.BrandName = properties["brandName"];
term.DisplayName = properties["displayName"];
term.Synonym = properties["synonym"];
term.FullName = properties["fullName"];
term.FullGenericName = properties["fullGenericName"];
term.Strength = properties["strength"];

Sin embargo, es importante tener en cuenta que las System.Json herramientas cargan la totalidad de los datos en la memoria.

JSON.NET

La biblioteca NewtonSoft JSON.NET es una biblioteca ampliamente utilizada para serializar y deserializar mensajes JSON. En el ejemplo de código siguiente se muestra cómo usar JSON.NET para deserializar un mensaje JSON en un objeto de C#:

var term = new RxTerm();
var properties = JObject.Parse(json)["rxtermsProperties"];
term.BrandName = properties["brandName"].Value<string>();
term.DisplayName = properties["displayName"].Value<string>();
term.Synonym = properties["synonym"].Value<string>();;
term.FullName = properties["fullName"].Value<string>();;
term.FullGenericName = properties["fullGenericName"].Value<string>();;
term.Strength = properties["strength"].Value<string>();
term.RxCUI = properties["rxcui"].Value<string>();

ServiceStack.Text

ServiceStack.Text es una biblioteca de serialización JSON diseñada para trabajar con la biblioteca ServiceStack. En el ejemplo de código siguiente se muestra cómo analizar JSON mediante :ServiceStack.Text.JsonObject

var result = JsonObject.Parse(json).Object("rxtermsProperties")
       .ConvertTo(x => new RxTerm {
               BrandName = x.Get("brandName"),
               DisplayName = x.Get("displayName"),
               Synonym = x.Get("synonym"),
               FullName = x.Get("fullName"),
               FullGenericName = x.Get("fullGenericName"),
               Strength = x.Get("strength"),
               RxTermDoseForm = x.Get("rxtermsDoseForm"),
               Route = x.Get("route"),
               RxCUI = x.Get("rxcui"),
               RxNormDoseForm = x.Get("rxnormDoseForm"),
       });

System.Xml.Linq

En caso de consumir un servicio web REST basado en XML, se puede usar LINQ to XML para analizar el XML y rellenar un objeto de C# insertado, como se muestra en el ejemplo de código siguiente:

var doc = XDocument.Parse(xml);
var result = doc.Root.Descendants("rxtermsProperties")
.Select(x=> new RxTerm()
       {
               BrandName = x.Element("brandName").Value,
               DisplayName = x.Element("displayName").Value,
               Synonym = x.Element("synonym").Value,
               FullName = x.Element("fullName").Value,
               FullGenericName = x.Element("fullGenericName").Value,
               //bind more here...
               RxCUI = x.Element("rxcui").Value,
       });

servicio web de 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.

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 y esto incluye compatibilidad con muchas de las configuraciones de servicio ASMX estándar.

Generación de un proxy

Se debe generar un proxy para consumir un servicio ASMX, 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 como un documento del lenguaje de descripción de servicios web (WSDL) generado por el servicio web. El proxy se compila mediante Visual Studio para Mac o Visual Studio para agregar una referencia web para el servicio web a los proyectos específicos de la plataforma.

La dirección URL del servicio web puede ser un recurso de sistema de archivos local o de origen remoto hospedado accesible a través del prefijo de ruta file:/// de acceso, por ejemplo:

file:///Users/myUserName/projects/MyProjectName/service.wsdl

La dirección URL del servicio web puede ser un recurso de sistema de archivos local o de origen remoto hospedado accesible a través del prefijo de ruta de acceso del archivo.

Esto genera el proxy en la carpeta Referencias de servicio o web del proyecto. Dado que un proxy se genera código, no debe modificarse.

Agregar manualmente un proxy a un proyecto

Si tiene un proxy existente que se ha generado mediante herramientas compatibles, esta salida se puede consumir cuando se incluye como parte del proyecto. En Visual Studio para Mac, use la opción de menú Agregar archivos... para agregar el proxy. Además, esto requiere System.Web.Services.dll hacer referencia explícitamente mediante el cuadro de diálogo Agregar referencias... .

Consumo del proxy

Las clases de proxy generadas proporcionan métodos para consumir el servicio 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 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. El ejemplo de código siguiente muestra un ejemplo:

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

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 Task.Factory.FromAsync método . Este método crea un Task objeto que ejecuta el TodoService.EndGetTodoItems método una vez completado el TodoService.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.

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.

Para obtener más información sobre cómo consumir un servicio ASMX, consulte Consumo de un servicio web de ASP.NET (ASMX).

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.

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

  • 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 es importante comprender que WCF admite las mismas funcionalidades que proporciona ASMX: mensajes SOAP a través de HTTP.

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 . Además, la compatibilidad con WCF requiere el uso de herramientas solo disponibles en un entorno de Windows para generar el proxy.

Generación de un proxy

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).

Configuración del proxy

La configuración del proxy generado generalmente tomará dos argumentos de configuración (según SOAP 1.1/ASMX o WCF) durante la inicialización: la EndpointAddress información de enlace asociada, como se muestra en el ejemplo siguiente:

var binding = new BasicHttpBinding () {
       Name= "basicHttpBinding",
       MaxReceivedMessageSize = 67108864,
};

binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas() {
       MaxArrayLength = 2147483646,
       MaxStringContentLength = 5242880,
};

var timeout = new TimeSpan(0,1,0);
binding.SendTimeout= timeout;
binding.OpenTimeout = timeout;
binding.ReceiveTimeout = timeout;

client = new Service1Client (binding, new EndpointAddress ("http://192.168.1.100/Service1.svc"));

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.

Consumo del proxy

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. El ejemplo de código siguiente muestra un ejemplo:

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

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 Task.Factory.FromAsync método . Este 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.

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.

Para obtener más información sobre cómo consumir un servicio WCF, consulte Consumo de un servicio web de Windows Communication Foundation (WCF).

Uso de seguridad de transporte

Los servicios WCF pueden emplear seguridad de nivel de transporte para protegerse contra la interceptación de mensajes. La plataforma Xamarin admite enlaces que emplean seguridad de nivel de transporte mediante SSL. Sin embargo, puede haber casos en los que es posible que la pila necesite validar el certificado, lo que da lugar a un comportamiento imprevisto. La validación se puede invalidar registrando un ServerCertificateValidationCallback delegado antes de invocar el servicio, como se muestra en el ejemplo de código siguiente:

System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) => { return true; };

Esto mantiene el cifrado de transporte al omitir la validación del certificado del lado servidor. Sin embargo, este enfoque ignora eficazmente los problemas de confianza asociados al certificado y puede que no sea adecuado. Para obtener más información, consulte Uso de raíces de confianza respetuosamente en mono-project.com.

Uso de seguridad de credenciales de cliente

Los servicios WCF también pueden requerir que los clientes de servicio se autentiquen mediante credenciales. La plataforma de Xamarin no admite el protocolo WS-Security, lo que permite a los clientes enviar credenciales dentro del sobre del mensaje SOAP. Sin embargo, la plataforma de Xamarin admite la capacidad de enviar credenciales de autenticación básica HTTP al servidor especificando el :ClientCredentialType

basicHttpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;

A continuación, se pueden especificar credenciales de autenticación básicas:

client.ClientCredentials.UserName.UserName = @"foo";
client.ClientCredentials.UserName.Password = @"mrsnuggles";

Para obtener más información sobre la autenticación básica HTTP, aunque en el contexto de un servicio web REST, consulte Autenticación de un servicio web RESTful.