Introducción a los servicios web
En esta guía se muestra cómo consumir diferentes tecnologías de servicio web. Entre los temas tratados se incluyen la comunicación con los servicios REST, los servicios SOAP y 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 servicio 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) aplica 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. Puesto que ATS está habilitado de forma predeterminada en las aplicaciones creadas 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 no participar en ATS si no es posible usar el protocolo y proteger HTTPS la comunicación con los recursos de Internet. Esto se puede lograr actualizando el archivo Info.plist de la aplicación. Para obtener más información, vea 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 que sea el método principal para acceder a 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 subsecciones siguientes los analizan. Para obtener más información sobre cómo consumir un servicio REST, vea Consumo de un servicio web RESTful.
HttpClient
Las bibliotecas de cliente HTTP de Microsoft proporcionan la clase , que se usa para enviar y recibir solicitudes a través de HTTP. Proporciona funcionalidad para enviar solicitudes HTTP y recibir respuestas HTTP desde 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, incluidos el código de estado, los encabezados y el cuerpo. La clase representa el cuerpo HTTP y los encabezados de HttpContent contenido, como y Content-TypeContent-Encoding . El contenido se puede leer mediante cualquiera de los métodos, como ReadAs y , en función del formato de los ReadAsStringAsyncReadAsByteArrayAsync datos.
Para obtener más información sobre la HttpClient clase , vea Creating the HttpClient.
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.
- Recuperación de
HttpWebResponsede la solicitud. - Lectura de datos fuera de la respuesta.
Por ejemplo, el código siguiente recupera datos del servicio web de la Biblioteca Nacional de Médicos 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 HttpWebRequest un objeto que devolverá datos con formato JSON. Los datos se devuelven en HttpWebResponse un , desde el que se puede obtener un para leer los StreamReader datos.
RestSharp
Otro enfoque para consumir servicios REST es usar la biblioteca RestSharp. RestSharp encapsula las solicitudes HTTP, incluida la compatibilidad con la recuperación de resultados como contenido de cadena sin formato o como un objeto de C# deserializador. Por ejemplo, el código siguiente realiza una solicitud al servicio web de la Biblioteca Nacional de Médicos de Estados Unidos 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 deserializar los datos devueltos desde los 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 , y las bibliotecas de C# de terceros, como RestSharp, las clases específicas de la plataforma también están disponibles para consumir HttpWebRequest servicios web. Por ejemplo, en iOS, se pueden NSUrlConnection usar las clases y NSMutableUrlRequest .
En el ejemplo de código siguiente se muestra cómo llamar al servicio web de la Biblioteca Nacional de Médicos 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 está portiendo 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 Service Stack IServiceClient.GetAsync 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 herramientas como ServiceStack y RestSharp hacen que sea fácil llamar y consumir servicios REST, a veces no es trivial consumir XML o JSON que no se ajuste 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
Los servicios web RESTful suelen usar mensajes JSON para devolver datos al cliente. JSON es un formato de intercambio de datos basado en texto que genera cargas compactas, lo que reduce los requisitos de ancho de banda 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 Xamarin se incluye de forma integrada con compatibilidad con JSON. 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 herramientas System.Json cargan la totalidad de los datos en la memoria.
JSON.NET
La biblioteca de JSON.NET NewtonSoft es una biblioteca ampliamente utilizada para serializar y deserializar mensajes JSON. En el ejemplo de código siguiente se muestra cómo 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 funcionar 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, LINQ to XML se puede usar para analizar el XML y rellenar un objeto de C# en línea, 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,
});
ASP.NET Web Service (ASMX)
ASMX proporciona la capacidad de compilar 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 los 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 saber 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, incluidos 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.
Generación de un proxy
Se debe generar un proxy para consumir un servicio ASMX, lo que permite que la aplicación se conecte al servicio. El proxy se construye mediante el consumo de metadatos de servicio que definen los métodos y la configuración de servicio asociada. Estos metadatos se exponen como un documento WSDL (Lenguaje de descripción de servicios Web) generado por el servicio web. El proxy se basa en 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
Esto genera el proxy en la carpeta Web o Referencias de servicio del proyecto. Puesto que se genera código de proxy, no se debe modificar.
Agregar manualmente un proxy a un Project
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 que System.Web.Services.dll se haga 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 usa 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 comienza la operación asincrónica y devuelve un objeto que implementa la 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 TPL (Task Parallel Library) puede simplificar el proceso de consumo de un par de métodos de inicio y finalización de APM encapsulando 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 objeto que ejecuta el método una vez completado el método, con el parámetro que indica que no se pasa ningún dato TaskTodoService.EndGetTodoItems al TodoService.BeginGetTodoItemsnullBeginGetTodoItems delegado. Por último, el valor de la enumeración especifica que se debe usar el comportamiento predeterminado para la creación y TaskCreationOptions ejecución de tareas.
Para obtener más información sobre APM, vea Modelo de programación asincrónica y TPL y Programación .NET Framework programación asincrónica en MSDN.
Para obtener más información sobre cómo consumir un servicio ASMX, vea Consumir un ASP.NET Web Service (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, con transacciones e interoperables.
WCF describe un servicio con una variedad de contratos diferentes, entre los que se incluyen los siguientes:
- Contratos de datos: definen las estructuras de datos que forman la base del contenido de un mensaje.
- Contratos de mensajes: redactar mensajes a partir de contratos de datos existentes.
- Contratos de error: permiten 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 Web Services (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 por 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 Windows para generar el proxy.
Generación de un proxy
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 definen los métodos y la configuración de servicio asociada. Estos metadatos se exponen en forma de documento WSDL (Lenguaje de descripción de servicios Web) generado por el servicio web. El proxy se puede crear mediante el Microsoft WCF Web Service Reference Provider de 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 Microsoft WCF Web Service Reference Provider en Visual Studio 2017 es usar la herramienta utilidad de metadatos 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: y/o la información de enlace asociada, como se muestra en el ejemplo EndpointAddress 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 servicios se comuniquen entre sí. especifica que los mensajes SOAP codificados por texto BasicHttpBinding se enviarán a través del protocolo de transporte HTTP. La especificación de una dirección de punto de conexión permite a la aplicación conectarse a distintas 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 comienza la operación asincrónica y devuelve un objeto que implementa la 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 TPL (Task Parallel Library) puede simplificar el proceso de consumo de un par de métodos de inicio y finalización de APM encapsulando 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 objeto que ejecuta el método una vez completado el método, con el parámetro que indica que no se pasa ningún dato TaskTodoServiceClient.EndGetTodoItems al TodoServiceClient.BeginGetTodoItemsnullBeginGetTodoItems delegado. Por último, el valor de la enumeración especifica que se debe usar el comportamiento predeterminado para la creación y TaskCreationOptions ejecución de tareas.
Para obtener más información sobre APM, vea Modelo de programación asincrónica y TPL y Programación .NET Framework programación asincrónica en MSDN.
Para obtener más información sobre cómo consumir un servicio WCF, vea Consume a Windows Communication Foundation (WCF) Web Service.
Uso de la 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 la pila necesite validar el certificado, lo que da lugar a un comportamiento imprevisto. La validación se puede invalidar registrando un delegado antes de invocar el servicio, como se ServerCertificateValidationCallback 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 tiempo que se ignora 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, vea Usar raíces de confianza de forma confermono-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 Xamarin no admite el protocolo WS-Security, que permite a los clientes enviar credenciales dentro del sobre de mensajes SOAP. Sin embargo, la plataforma Xamarin admite la capacidad de enviar credenciales de autenticación básica HTTP al servidor mediante la especificación de la ClientCredentialType adecuada:
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, vea Authenticating a RESTful Web Service.
