Introdução aos serviços Web

Este guia demonstra como consumir diferentes tecnologias de serviço Web. Os tópicos abordados incluem comunicação com serviços REST, serviços SOAP e serviços do Windows Communication Foundation.

Para funcionar corretamente, muitos aplicativos móveis dependem da nuvem e, portanto, a integração de serviços Web em aplicativos móveis é um cenário comum. A plataforma Xamarin dá suporte ao consumo de diferentes tecnologias de serviço Web e inclui suporte interno e de terceiros para consumir serviços RESTful, ASMX e WCF (Windows Communication Foundation).

Para clientes que usam o Xamarin.Forms, há exemplos completos usando cada uma dessas tecnologias na documentação dos Serviços Web do Xamarin.Forms .

Importante

No iOS 9, a ATS (Segurança de Transporte de Aplicativo) impõe conexões seguras entre recursos da Internet (como o servidor de back-end do aplicativo) e o aplicativo, impedindo assim a divulgação acidental de informações confidenciais. Como o ATS é habilitado por padrão em aplicativos criados para iOS 9, todas as conexões estarão sujeitas aos requisitos de segurança do ATS. Se as conexões não atenderem a esses requisitos, elas falharão com uma exceção.

Você poderá recusar a ATS se não for possível usar o HTTPS protocolo e a comunicação segura para recursos da Internet. Isso pode ser feito atualizando o arquivo Info.plist do aplicativo. Para obter mais informações, consulte Segurança do Transporte de Aplicativo.

REST

REST (Transferência de Estado Representacional) é um estilo de arquitetura para a criação de serviços Web. As solicitações REST são feitas por HTTP usando os mesmos verbos HTTP que os navegadores da Web usam para recuperar páginas da Web e enviar dados para os servidores. Os verbos são:

  • GET – essa operação é usada para recuperar dados do serviço Web.
  • POST – essa operação é usada para criar um item de dados no serviço Web.
  • PUT – essa operação é usada para atualizar um item de dados no serviço Web.
  • PATCH – essa operação é usada para atualizar um item de dados no serviço Web, descrevendo um conjunto de instruções sobre como o item deve ser modificado. Esse verbo não é usado no aplicativo de exemplo.
  • DELETE – essa operação é usada para excluir um item de dados no serviço Web.

APIs de serviço Web que aderem ao REST são chamadas de APIs RESTful e são definidas usando:

  • Um URI de base.
  • Métodos HTTP, como GET, POST, PUT, PATCH ou DELETE.
  • Um tipo de mídia para os dados, como JavaScript Object Notation (JSON).

A simplicidade do REST ajudou a torná-lo o método principal para acessar serviços Web em aplicativos móveis.

Consumindo serviços REST

Há várias bibliotecas e classes que podem ser usadas para consumir serviços REST e as subseções a seguir as discutem. Para obter mais informações sobre como consumir um serviço REST, consulte Consumir um serviço Web RESTful.

HttpClient

As Bibliotecas de Cliente HTTP da Microsoft fornecem a HttpClient classe , que é usada para enviar e receber solicitações por HTTP. Ele fornece funcionalidade para enviar solicitações HTTP e receber respostas HTTP de um recurso identificado pelo URI. Cada solicitação é enviada como uma operação assíncrona. Para obter mais informações sobre operações assíncronas, consulte Visão geral do suporte assíncrono.

A HttpResponseMessage classe representa uma mensagem de resposta HTTP recebida do serviço Web depois que uma solicitação HTTP é feita. Ele contém informações sobre a resposta, incluindo o código status, cabeçalhos e corpo. A HttpContent classe representa o corpo HTTP e os cabeçalhos de conteúdo, como Content-Type e Content-Encoding. O conteúdo pode ser lido usando qualquer um dos ReadAs métodos, como ReadAsStringAsync e ReadAsByteArrayAsync, dependendo do formato dos dados.

Para obter mais informações sobre a HttpClient classe , consulte Criando o objeto HTTPClient.

HTTPWebRequest

Chamar serviços Web com HTTPWebRequest envolve:

  • Criando a instância de solicitação para um URI específico.
  • Definindo várias propriedades HTTP na instância de solicitação.
  • Recuperando um HttpWebResponse da solicitação.
  • Lendo dados da resposta.

Por exemplo, o código a seguir recupera dados do serviço Web da Biblioteca Nacional de Medicina dos EUA:

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);
  }
}

O exemplo acima cria um HttpWebRequest que retornará dados formatados como JSON. Os dados são retornados em um HttpWebResponse, do qual um StreamReader pode ser obtido para ler os dados.

RestSharp

Outra abordagem para consumir serviços REST é usar a biblioteca RestSharp . RestSharp encapsula solicitações HTTP, incluindo suporte para recuperar resultados como conteúdo de cadeia de caracteres bruto ou como um objeto C# desserializado. Por exemplo, o código a seguir faz uma solicitação para o serviço Web da Biblioteca Nacional de Medicina dos EUA e recupera os resultados como uma cadeia de caracteres formatada em 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 é um método que pegará a cadeia de caracteres JSON bruta da propriedade e a RestSharp.RestResponse.Content converterá em um objeto C#. A desserialização dos dados retornados dos serviços Web é discutida posteriormente neste artigo.

Nsurlconnection

Além das classes disponíveis na BCL (biblioteca de classes base) Mono, como HttpWebRequest, e bibliotecas C# de terceiros, como RestSharp, classes específicas da plataforma também estão disponíveis para consumir serviços Web. Por exemplo, no iOS, as NSUrlConnection classes e NSMutableUrlRequest podem ser usadas.

O exemplo de código a seguir mostra como chamar o serviço Web da Biblioteca Nacional de Medicina dos EUA usando classes 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();
       }
}

Em geral, classes específicas da plataforma para consumir serviços Web devem ser limitadas a cenários em que o código nativo está sendo portado para C#. Sempre que possível, o código de acesso do serviço Web deve ser portátil para que possa ser compartilhado entre plataformas.

ServiceStack

Outra opção para chamar serviços Web é a biblioteca da Pilha de Serviços . Por exemplo, o código a seguir mostra como usar o método do IServiceClient.GetAsync Service Stack para emitir uma solicitação de serviço:

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

Importante

Embora ferramentas como ServiceStack e RestSharp facilitem chamar e consumir serviços REST, às vezes não é trivial consumir XML ou JSON que não está em conformidade com as convenções de serialização padrão do DataContract . Se necessário, invoque a solicitação e manipule a serialização apropriada explicitamente usando a biblioteca ServiceStack.Text discutida abaixo.

Consumindo dados RESTful

Os serviços Web RESTful normalmente usam mensagens JSON para retornar dados ao cliente. JSON é um formato de intercâmbio de dados baseado em texto que produz cargas compactas, o que resulta em requisitos de largura de banda reduzidos ao enviar dados. Nesta seção, os mecanismos para consumir respostas RESTful em JSON e POX (Plain-Old-XML) serão examinados.

System.JSON

A plataforma Xamarin é fornecida com suporte para JSON pronto para uso. Usando um JsonObject, os resultados podem ser recuperados conforme mostrado no exemplo de código a seguir:

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"];

No entanto, é importante estar ciente de que as System.Json ferramentas carregam a totalidade dos dados na memória.

JSON.NET

A biblioteca de JSON.NET NewtonSoft é uma biblioteca amplamente usada para serializar e desserializar mensagens JSON. O exemplo de código a seguir mostra como usar JSON.NET para desserializar uma mensagem JSON em um objeto 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 é uma biblioteca de serialização JSON projetada para funcionar com a biblioteca ServiceStack. O exemplo de código a seguir mostra como analisar JSON usando um 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

No caso de consumir um serviço Web REST baseado em XML, LINQ to XML pode ser usado para analisar o XML e preencher um objeto C# embutido, conforme demonstrado no exemplo de código a seguir:

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,
       });

SERVIÇO Web ASP.NET (ASMX)

O ASMX fornece a capacidade de criar serviços Web que enviam mensagens usando o SOAP (Simple Object Access Protocol). SOAP é um protocolo independente de plataforma e independente de linguagem para criar e acessar serviços Web. Os consumidores de um serviço ASMX não precisam saber nada sobre a plataforma, o modelo de objeto ou a linguagem de programação usada para implementar o serviço. Eles só precisam entender como enviar e receber mensagens SOAP.

Uma mensagem SOAP é um documento XML que contém os seguintes elementos:

  • Um elemento raiz chamado Envelope que identifica o documento XML como uma mensagem SOAP.
  • Um elemento Header opcional que contém informações específicas do aplicativo, como dados de autenticação. Se o elemento Header estiver presente, ele deverá ser o primeiro elemento filho do elemento Envelope .
  • Um elemento Body necessário que contém a mensagem SOAP destinada ao destinatário.
  • Um elemento Fault opcional usado para indicar mensagens de erro. Se o elemento Fault estiver presente, ele deverá ser um elemento filho do elemento Body .

O SOAP pode operar em vários protocolos de transporte, incluindo HTTP, SMTP, TCP e UDP. No entanto, um serviço ASMX só pode operar por HTTP. A plataforma Xamarin dá suporte a implementações soap 1.1 padrão por HTTP, e isso inclui suporte para muitas das configurações de serviço ASMX padrão.

Gerando um proxy

Um proxy deve ser gerado para consumir um serviço ASMX, o que permite que o aplicativo se conecte ao serviço. O proxy é construído consumindo metadados de serviço que definem os métodos e a configuração de serviço associada. Esses metadados são expostos como um documento WSDL (Linguagem de Descrição dos Serviços Web) gerado pelo serviço Web. O proxy é criado usando Visual Studio para Mac ou Visual Studio para adicionar uma referência da Web para o serviço Web aos projetos específicos da plataforma.

A URL do serviço Web pode ser uma fonte remota hospedada ou um recurso do sistema de arquivos local acessível por meio do prefixo de file:/// caminho, por exemplo:

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

A URL do serviço Web pode ser uma fonte remota hospedada ou um recurso do sistema de arquivos local acessível por meio do prefixo do caminho do arquivo

Isso gera o proxy na pasta Referências de Serviço ou Web do projeto. Como um proxy é gerado, ele não deve ser modificado.

Adicionar manualmente um proxy a um projeto

Se você tiver um proxy existente que foi gerado usando ferramentas compatíveis, essa saída poderá ser consumida quando incluída como parte do projeto. Em Visual Studio para Mac, use a opção de menu Adicionar arquivos... para adicionar o proxy. Além disso, isso requer queSystem.Web.Services.dll sejam referenciados explicitamente usando a caixa de diálogo Adicionar Referências... .

Consumindo o proxy

As classes de proxy geradas fornecem métodos para consumir o serviço Web que usa o padrão de design APM (Modelo de Programação Assíncrona). Nesse padrão, uma operação assíncrona é implementada como dois métodos chamados BeginOperationName e EndOperationName, que iniciam e encerram a operação assíncrona.

O método BeginOperationName inicia a operação assíncrona e retorna um objeto que implementa a IAsyncResult interface. Depois de chamar BeginOperationName, um aplicativo pode continuar executando instruções sobre o thread de chamada, enquanto a operação assíncrona ocorre em um thread de pool de threads.

Para cada chamada para BeginOperationName, o aplicativo também deve chamar EndOperationName para obter os resultados da operação. O valor retornado de EndOperationName é o mesmo tipo retornado pelo método de serviço Web síncrono. O código a seguir mostra um exemplo disso:

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

A TPL (Biblioteca Paralela de Tarefas) pode simplificar o processo de consumo de um par de métodos de início/término do APM encapsulando as operações assíncronas no mesmo Task objeto. Esse encapsulamento é fornecido por várias sobrecargas do Task.Factory.FromAsync método . Esse método cria um Task que executa o TodoService.EndGetTodoItems método depois que o TodoService.BeginGetTodoItems método é concluído, com o null parâmetro indicando que nenhum dado está sendo passado para o BeginGetTodoItems delegado. Por fim, o valor da TaskCreationOptions enumeração especifica que o comportamento padrão para a criação e execução de tarefas deve ser usado.

Para obter mais informações sobre o APM, consulte Modelo de Programação Assíncrona e TPL e Programação Assíncrona .NET Framework Tradicional no MSDN.

Para obter mais informações sobre como consumir um serviço ASMX, consulte Consumir um SERVIÇO Web ASP.NET (ASMX).

Windows Communication Foundation (WCF)

O WCF é a estrutura unificada da Microsoft para a criação de aplicativos orientados a serviços. Ele permite que os desenvolvedores criem aplicativos distribuídos seguros, confiáveis, transacionados e interoperáveis.

O WCF descreve um serviço com uma variedade de contratos diferentes que incluem o seguinte:

  • Contratos de dados – defina as estruturas de dados que formam a base para o conteúdo dentro de uma mensagem.
  • Contratos de mensagens – redigir mensagens de contratos de dados existentes.
  • Contratos de falha – permitem que falhas SOAP personalizadas sejam especificadas.
  • Contratos de serviço – especifique as operações que os serviços dão suporte e as mensagens necessárias para interagir com cada operação. Eles também especificam qualquer comportamento de falha personalizado que possa ser associado a operações em cada serviço.

Há diferenças entre ASP.NET SERVIÇOs Web (ASMX) e WCF, mas é importante entender que o WCF dá suporte aos mesmos recursos que o ASMX fornece – mensagens SOAP por HTTP.

Importante

O suporte da plataforma Xamarin para WCF é limitado a mensagens SOAP codificadas em texto por HTTP/HTTPS usando a BasicHttpBinding classe . Além disso, o suporte ao WCF requer o uso de ferramentas disponíveis apenas em um ambiente do Windows para gerar o proxy.

Gerando um proxy

Um proxy deve ser gerado para consumir um serviço WCF, o que permite que o aplicativo se conecte ao serviço. O proxy é construído consumindo metadados de serviço que definem os métodos e a configuração de serviço associada. Esses metadados são expostos na forma de um documento WSDL (Linguagem de Descrição dos Serviços Web) gerado pelo serviço Web. O proxy pode ser criado usando o Provedor do Microsoft WCF Web Service Reference no Visual Studio 2017 para adicionar uma referência de serviço para o serviço Web a uma Biblioteca .NET Standard.

Uma alternativa para criar o proxy usando o Provedor do Microsoft WCF Web Service Reference no Visual Studio 2017 é usar a Ferramenta de Utilitário de Metadados serviceModel (svcutil.exe). Para obter mais informações, consulte ServiceModel Metadata Utility Tool (Svcutil.exe).

Configurando o Proxy

A configuração do proxy gerado geralmente terá dois argumentos de configuração (dependendo de SOAP 1.1/ASMX ou WCF) durante a inicialização: as EndpointAddress informações de associação e/ou associadas, conforme mostrado no exemplo abaixo:

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"));

Uma associação é usada para especificar os detalhes de transporte, codificação e protocolo necessários para que aplicativos e serviços se comuniquem entre si. O BasicHttpBinding especifica que as mensagens SOAP codificadas em texto serão enviadas pelo protocolo de transporte HTTP. Especificar um endereço de ponto de extremidade permite que o aplicativo se conecte a diferentes instâncias do serviço WCF, desde que haja várias instâncias publicadas.

Consumindo o proxy

As classes de proxy geradas fornecem métodos para consumir os serviços Web que usam o padrão de design APM (Modelo de Programação Assíncrona). Nesse padrão, uma operação assíncrona é implementada como dois métodos chamados BeginOperationName e EndOperationName, que iniciam e encerram a operação assíncrona.

O método BeginOperationName inicia a operação assíncrona e retorna um objeto que implementa a IAsyncResult interface. Depois de chamar BeginOperationName, um aplicativo pode continuar executando instruções sobre o thread de chamada, enquanto a operação assíncrona ocorre em um thread de pool de threads.

Para cada chamada para BeginOperationName, o aplicativo também deve chamar EndOperationName para obter os resultados da operação. O valor retornado de EndOperationName é o mesmo tipo retornado pelo método de serviço Web síncrono. O código a seguir mostra um exemplo disso:

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

A TPL (Biblioteca Paralela de Tarefas) pode simplificar o processo de consumo de um par de métodos de início/término do APM encapsulando as operações assíncronas no mesmo Task objeto. Esse encapsulamento é fornecido por várias sobrecargas do Task.Factory.FromAsync método . Esse método cria um Task que executa o TodoServiceClient.EndGetTodoItems método depois que o TodoServiceClient.BeginGetTodoItems método é concluído, com o null parâmetro indicando que nenhum dado está sendo passado para o BeginGetTodoItems delegado. Por fim, o valor da TaskCreationOptions enumeração especifica que o comportamento padrão para a criação e execução de tarefas deve ser usado.

Para obter mais informações sobre o APM, consulte Modelo de Programação Assíncrona e TPL e Programação Assíncrona .NET Framework Tradicional no MSDN.

Para obter mais informações sobre como consumir um serviço WCF, consulte Consumir um serviço Web do WCF (Windows Communication Foundation).

Usando a segurança de transporte

Os Serviços do WCF podem empregar segurança em nível de transporte para proteger contra interceptação de mensagens. A plataforma Xamarin dá suporte a associações que empregam segurança em nível de transporte usando SSL. No entanto, pode haver casos em que a pilha pode precisar validar o certificado, o que resulta em um comportamento imprevisto. A validação pode ser substituída registrando um ServerCertificateValidationCallback delegado antes de invocar o serviço, conforme demonstrado no exemplo de código a seguir:

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

Isso mantém a criptografia de transporte ignorando a validação do certificado do lado do servidor. No entanto, essa abordagem ignora efetivamente as preocupações de confiança associadas ao certificado e pode não ser apropriada. Para obter mais informações, consulte Usando raízes confiáveis respeitosamente em mono-project.com.

Usando a Segurança de Credencial do Cliente

Os serviços do WCF também podem exigir que os clientes de serviço se autentiquem usando credenciais. A plataforma Xamarin não dá suporte ao protocolo WS-Security, que permite que os clientes enviem credenciais dentro do envelope de mensagem SOAP. No entanto, a plataforma Xamarin dá suporte à capacidade de enviar credenciais de Autenticação Básica HTTP para o servidor especificando o apropriado ClientCredentialType:

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

Em seguida, as credenciais básicas de autenticação podem ser especificadas:

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

Para obter mais informações sobre a autenticação básica HTTP, embora no contexto de um serviço Web REST, consulte Autenticando um serviço Web RESTful.