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 HttpWebResponse
objeto , 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 HttpWebRequest
las 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
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.