Compartilhar via


Este artigo foi traduzido por máquina.

Estação de serviço

Criando clientes RESTful

Jon Flanders

Baixar o código de exemplo

Nesta edição do serviço Estação RESTful, Falarei sobre clientes de construção em relação a serviços RESTful. Criação de clientes é percebida tão difícil (principalmente devido à falta de geração automática do cliente de metadados, à la SOAP e WSDL), mas, na verdade é como qualquer outro tipo de código que você escreve: No primeiro há algum tempo rampa-up como você se acostuma a um paradigma de programação específico. Em seguida, depois de ter a paralisação desse paradigma, escrever código contra ele fica mais fácil e mais fácil. Descobri isso seja true para gravar clientes contra serviços RESTful, que tenham muitos outros desenvolvedores que você interagir com.

Noções básicas do cliente

Vou mostrar as noções básicas de interação entre um cliente e um serviço REST usando. Apenas um recapitular rápido sobre o REST antes de nos aprofundar. REST é um estilo de arquitetura que se baseia nos mesmos princípios que fornecem a base da World Wide Web. Os clientes interagem com serviços fazendo solicitações HTTP e serviços respondem com respostas HTTP, que freqüentemente incluem uma representação de um recurso que pode usar código do cliente para um aplicativo da unidade.

Imagine que eu tenha um serviço expõe informações e funcionalidades relacionados ao Hyper-V (a tecnologia de virtualização incorporada no Windows Server 2008). Se a "entrada"URI deste serviço foi http://localhost/HyperVServices/VMs.svc/, como um cliente, faria uma solicitação HTTP GET para recuperar uma representação do recurso identificado por esse URI. Nesse caso, o recurso é formatado como XML e representa uma lista de todos os s máquinas virtuais (VM) instalado em um determinado computador.


A Figura 1 um HTTP Simple solicitação GET

Conforme mostrei no primeiro dos meus artigos de estação de serviço sobre o REST, na edição de janeiro de 2009 da MSDN Magazine (msdn.microsoft.com/magazine/2009.01.servicestation.aspx), um dos benefícios pequenos de usar o que REST é que você pode usar ferramentas de solicitação HTTP para tornar suas solicitações de teste inicial contra um serviço. (Usando estas ferramentas para depurar problemas também é muito útil.) Nesse caso, Estou usando uma ferramenta gratuita chamada Fiddler (fiddlertool.com) para fazer uma solicitação HTTP GET para o recurso que representa todas as minhas máquinas virtuais. Consulte do Figura 1. Obviamente, ferramentas como o Fiddler são importantes, mas para criar um aplicativo que você precise escrever código contra o serviço. Vou começar com as noções básicas de fazer um HTTP solicitar e obter para o problema de leitura, na verdade, o recurso.

O .NET Framework sempre ofereceu uma API de HTTP básica que pode ser usado para interagir com serviços RESTful. No centro dessa API são os tipos HttpWebRequest e HttpWebResponse. Para fazer uma solicitação HTTP, criar e configurar uma instância de HttpWebRequest e, em seguida, solicitar o HttpWebResponse. A Figura 2 mostra um exemplo. Faz uma solicitação para um serviço RESTful é um simples conjunto de etapas:

  1. Certifique-se que você está usando o URI correto.
  2. Certifique-se que você está usando o método correto (verbo HTTP).
  3. Processe a resposta se o código de status é 200 OK.
  4. Lidar com outros códigos de status como apropriado (mais sobre isso posteriormente).

Como as etapas 1 e 2 são muito fácil, geral etapa 3 (e às vezes, a etapa 4) acabam sendo mais difícil das etapas.

A maioria do trabalho da etapa 3 está processando a representação de recurso de alguma maneira razoável. Como XML ainda é a resposta mais comuns do recurso, vou abordar que neste artigo. (O outro tipo de mídia provavelmente seria JSON). Quando o recurso for XML, você precisa analisar esse recurso e escrever o código que pode extrair os dados que necessários do .NET que resource.Using, você tem algumas opções para análise de XML. Há a classe XmlReader testadas e true. A classe XmlDocument também é uma possibilidade e usando um desses tipos você manualmente pode analisar ou usá-lo para navegar as utilidades o XML. Com o advento do .NET 3.0, você também obtém o XDocument classe, que, quando combinado com LINQ para XML, se tornou a opção de fato para processamento XML. Figura 3 mostra um exemplo de código processar a lista de VMs usando XDocument.

Figura 2 simples código HttpWebRequest

string uri = "http://localhost/HyperVServices/VMs.svc/";
var webRequest = (HttpWebRequest)WebRequest.Create(uri);
//this is the default method/verb, but it's here for clarity
webRequest.Method = "GET";
var webResponse = (HttpWebResponse)webRequest.GetResponse();
Console.WriteLine("Response returned with status code of 0}",
webResponse.StatusCode);
if (webResponse.StatusCode == HttpStatusCode.OK)
ProcessOKResponse(webResponse);
else
ProcessNotOKResponse(webResponse);

O LINQ para XML, juntamente com tipos anônimos, fornece uma maneira boa e fácil para lidar com transformar XML em objetos que um aplicativo pode processar e, claro, você também pode usar um tipo predefinido em vez de um tipo anônimo.

Outra abordagem é popular quando programação em serviços baseados em SOAP e baseados em REST é fazer com que as respostas a ser desserializado em tipos .NET automaticamente. No caso de SOAP, isso geralmente acontece no proxy gerado por WSDL. Com o Windows Communication Foundation (WCF) e REST, isso pode ser feito de duas maneiras. Uma maneira (que eu realmente não recomendável, mas estou mencionar quanto à completude) é usar a natureza simétrica do WCF para usar uma definição de contrato de serviço do WCF no cliente. Na verdade, o suporte a WCF REST inclui um tipo chamado WebChannelFactory que pode ser usado para criar um canal cliente contra uma definição de contrato de serviço. Não recomendo esse tipo de cliente de programação por dois motivos. Primeiro, criar o cliente torna-se uma operação muito manual e propensa a erro. Em segundo lugar, usar um contrato de serviço com rigidez de tipos cria uma grande união entre o cliente e o serviço. Evitar o acoplamento forte é um dos principais motivos que teve êxito na Web e desejamos continuar essa tendência quando usar a Web por meio de programação.

Outra maneira de usar a serialização XML é usar o método HttpWebResponse.GetResponseStream e desserializar o XML para o objeto manualmente. Você pode fazer isso usando o XmlSerializer ou o serializador DataContract do WCF. Na maioria dos casos, o XmlSerializer é a abordagem preferível como ele lida com mais variações em documentos XML (por exemplo, atributos e elementos nonqualified) que o serializador DataContract.

Figura 3 processamento uma resposta RESTful usando XDocument

var stream = webResponse.GetResponseStream();
var xr = XmlReader.Create(stream);
var xdoc = XDocument.Load(xr);
var vms = from v in xdoc.Root.Elements("VM")
select new { Name = v.Element("Name").Value};
foreach (var item in vms)
{
Console.WriteLine(item.Name);
}

O problema ainda parece círculo volta para o fato de que serviços RESTful geralmente não expõem os metadados que podem ser usados para autogenerate alguns ou todos os esse código. Embora muitos no camp RESTful não vir isso como um problema (novamente, nada que autogenerates contra serviço definições podem ser vistas como o agente de acoplamento forte nocivo), certamente, há ocasiões quando ter essas ferramentas não é um empecilho para a adoção do REST.

Uma abordagem interessante realizada pelo WCF REST Starter Kit (asp.net/downloads/starter-kits/wcf-rest/ de), que é um aprimoramento da Microsoft do REST modelo no .NET 3.5, de programação é fornecer um recurso autogeneration parcial. Se você instalar o starter kit, você terá um novo item de menu no Visual Studio 2008 no menu Editar, como você pode ver no Figura 4.


Figura 4 colar XML como tipos de item de menu

O modelo de uso deste comando é muito simples. Copiar a representação de recurso XML na área de transferência (de alguma documentação legível o serviço expõe ou fazendo uma solicitação com uma ferramenta como o Fiddler). Depois de fazer isso, um conjunto de tipos XmlSerializable é criado, você pode usar para transformar o fluxo do HttpWebResponse em um objeto. Consulte de Figura 5 (o corpo de tipos gerados não é mostrado para fins de brevidade).

Figura 5 código gerado de colar usando XML como tipos

var stream = webResponse.GetResponseStream();
//in a real app you'd want to cache this object
var xs = new XmlSerializer(typeof(VMs));
var vms = xs.Deserialize(stream) as VMs;
foreach (VMsVM vm in vms.VM)
{
Console.WriteLine(vm.Name);
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "",
IsNullable = false)]
public partial class VMs
{
private VMsVM[] vmField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("VM")]
public VMsVM[] VM
{
get
{
return this.vmField;
}
set
{
this.vmField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "2.0.50727.4918")]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class VMsVM
{
//omitted
}

O Starter Kit REST não apenas simplifica o uso de XmlSerializer, mas ele também fornece uma API muito interessante no topo da API HttpWebRequest/WebResponse. O código a seguir mostra a solicitação GET simples da Figura 1 reescrito usando HttpClient API do REST Kit do Iniciante:

 

string uri = "http://localhost/HyperVServices/VMs.svc/";
var client = new HttpClient();
var message = client.Get(uri);
ProcessOKResponse(message.Content.ReadAsStream());

A classe HttpClient bastante simplifica (e torna explícito que você estiver usando os verbos) o uso da API de HTTP .NET. Além disso, como você pode ver no código a seguir, ele simplifica o uso do recurso Colar como tipos XML gerado:

var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
message.Content);

Embora o REST Starter Kit oficialmente não suportado pela Microsoft ainda, ele ilustrar um ponto muito importante: Esse cliente REST programação pode ser simplificado e automatizado parcialmente sem um serviço RESTful com metadados concluído à la um arquivo WSDL.

Usando HTTP

Uma maneira que um cliente e um serviço podem tirar proveito do HTTP é corretamente usar códigos de status e cabeçalhos. Obter mais informações sobre códigos de status podem ser encontradas em de w3.org/Protocols/rfc2616/rfc2616-sec10.html.

Como eu já não existe mais em todo o mundo falando sobre o REST, uma das vantagens dos serviços RESTful que eu geralmente destaque é que solicitações GET podem ser armazenada em cache. Não se engane, a escalabilidade e o sucesso da Web é amplamente por causa de cache HTTP.

Uma maneira de tirar proveito de HTTP cache é usar o GET condicional. GET condicional permite que o cliente ("agente de usuário"No jargão do HTTP) para fazer uma solicitação GET para um recurso que o agente de usuário já possui uma cópia de. Se o recurso não tenha alterado, o servidor informa ao agente do usuário que o recurso é exatamente o mesmo que a versão já mantida pelo agente do usuário. A vantagem de eficiência de GET condicional é uma redução no uso de largura de banda da rede entre o servidor e o agente de usuário, liberando a largura de banda a ser usado por solicitações para recursos recém-criados ou modificados. Além disso, ele salva o tempo de processamento adicional necessário para serializar o recurso, embora não o processamento de tempo para gerar ou recuperar o recurso (desde que você precise de uma cópia do recurso atual para compará-la com as informações enviadas pelo agente do usuário com o GET condicional).

Figura 6 implementação do lado do servidor de GET condicional

[OperationContract]
[WebGet(UriTemplate = "/{name}")]
public VMData GetOne(string name)
{
VMManager.Connect();
var v = VMManager.GetVirtualMachine(name);
var newVM = FromVM(v);
string etag = GenerateETag(newVM);
if (CheckETag(etag))
return null;
if (newVM == null)
{
OutgoingWebResponseContext ctx =
WebOperationContext.Current.OutgoingResponse;
ctx.SetStatusAsNotFound();
ctx.SuppressEntityBody = true;
}
SetETag(etag);
return newVM;
}
private bool CheckETag(string currentETag)
{
IncomingWebRequestContext ctx =
WebOperationContext.Current.IncomingRequest;
string incomingEtag =
ctx.Headers[HttpRequestHeader.IfNoneMatch];
if (incomingEtag != null)
{
if (currentETag == incomingEtag)
{
SetNotModified();
return true;
}
}
return false;
}
string GenerateETag(VMData vm)
{
byte[] bytes = Encoding.UTF8.GetBytes(vm.ID +
vm.LastChanged.ToString());
byte[] hash = MD5.Create().ComputeHash(bytes);
string etag = Convert.ToBase64String(hash);
return string.Format("\"{0}\"", etag);
}
void SetETag(string etag)
{
OutgoingWebResponseContext ctx =
WebOperationContext.Current.OutgoingResponse;
ctx.ETag = etag;
}

Como a maioria dos outros "avançada"Conceitos HTTP, o WCF não oferece suporte GET condicional automaticamente porque a implementação de GET condicional é altamente variável entre implementações de serviço. No entanto, como faz outros "avançada"Conceitos HTTP, o WCF fornece as ferramentas para implementar condicional GET. Há duas abordagens para fazer isso: usando a hora em que o recurso última modificação, ou usando um identificador exclusivo, conhecido como um ETag.

ETags tornaram-se a forma mais popular de implementar condição GET. Em do Figura 6, você pode ver o código para implementar um GET condicional com base em ETag no serviço VM. Observe que esta é a implementação do lado do servidor;Abordarei para o cliente em alguns instantes.

O fluxo básico é para o servidor para procurar o ETag no cabeçalho If-None-Match HTTP enviado pelo cliente e tentar correspondam a ETag atual gerado para o recurso. Nesse caso, Estou usando a identificação exclusiva de cada VM e o carimbo de hora última modificação (convertido em bytes e, em seguida, transformado em um hash MD5, que é uma implementação bastante comum). Se os dois valores coincidirem, o servidor de envia de volta um cabeçalho HTTP 304 não modificado com um corpo vazio, salvando o tempo de serialização, bem como largura de banda. O cliente obtém uma resposta muito mais rápida e sabe que ele pode usar o mesmo recurso já possui.


Figura 7 um cliente WPF VM simples

Imagine um aplicativo cliente como a mostrada na Figura 7. que este aplicativo mostra o nome de cada VM mais a imagem do status atual da máquina virtual. Agora imagine que você deseja atualizar este aplicativo para coincidir com o estado atual de cada VM. Para fazer isso, você teria que escrever algum código em um timer e atualizar cada registro se fosse diferente do seu registro local. E é provável que você poderia atualizar tudo durante cada iteração apenas para simplificar o aplicativo, mas isso seria uma perda de recursos.

Se você usou em vez disso, o GET condicional no cliente, você pode pesquisar o serviço para alterações enviando uma solicitação GET condicional, o que seria usar um cabeçalho If-None-Match HTTP para indicar o ETag do recurso. Para a coleção de VMs, o serviço pode usar o VM alterado mais recentemente para gerar ETag e o cliente atualizará somente se um ou mais as VMs alterou seu estado. (Se você tivesse muita VMs, convém fazer isso para cada VM. Mas como nós são ligação de dados com a coleção neste aplicativo, estou OK ao atualizar a coleção inteira).

Agora, implementar essa lógica não é extremamente difícil, mas é um dos recursos que implementa o REST Starter Kit para você. Incluído nos exemplos de código é que um arquivo nomeado PollingAgent.cs, que tem um cliente GET condicional automático que pesquisa um ponto de extremidade RESTful em um intervalo que você definir. Quando o PollingAgent determina que o recurso foi alterado (porque o serviço não está retornando um 302), ele aciona um retorno de chamada.

Portanto, no meu aplicativo simples do WPF, eu apenas levar o resultado desse retorno de chamada (que é o objeto HttpResponseMessage) e revincular Meus controles para os novos dados. Figura 8 mostra o código para implementar esse aplicativo usando o PollingAgent.

Seguir Hypertext

Antes de sair o assunto de clientes, quero tocar em outro as restrições do REST importantes: usando hipermídia como o mecanismo do estado do aplicativo (conhecido como HATEOAS).

Acho que HATEOAS é mais fácil de entender no contexto da Web humano. Ao usar a Web humana, às vezes precisamos indicadores sabemos que queremos visite sites. Dentro desses sites, no entanto, nós geralmente siga hiperlinks com base em nossas necessidades do dia. Pode ter um indicador para www.amazon.com, mas quando quero comprar algo, siga os links em cada página (recurso) para adicionar um item ao carrinho de meu. E siga os hiperlinks (e às vezes formulários) em cada página subseqüente para processar meu pedido. Em cada estágio de processamento de pedidos, os links na página representam o estado atual do aplicativo (em outras palavras, o que, como o agente de usuário, fazer?).

Uma consideração importante ao criar clientes RESTful (embora parte disso seja incumbent no serviço RESTful implementadores bem) é manter "indicadores"em um mínimo. O que isso significa que praticamente é que os clientes devem ter como pouco conhecimento possível sobre a estrutura URI de suas representações, mas tanto quanto possível eles devem saber como "navegar""hiperlinks"dentro desses recursos. Coloquei "hiperlinks"aspas como qualquer tipo de dados em seu recurso pode ser um URI relativo para a criação de "link" próximopara o cliente.

Figura 8 condicional GET usando o REST Starter Kit pesquisa agente

public Window1()
{
InitializeComponent();
string uri = "http://localhost/HyperVServices/VMs.svc/";
var client = new HttpClient();
var message = client.Get(uri);
var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
message.Content);
_vmList.DataContext = vms.VM;
var pa = new PollingAgent();
pa.HttpClient = client;
pa.ResourceChanged += new EventHandler<ConditionalGetEventArgs>(
pa_ResourceChanged);
pa.PollingInterval = new TimeSpan(0, 0, 5);
pa.StartPolling(new Uri(uri), message.Headers.ETag, null);
}
void pa_ResourceChanged(object sender, ConditionalGetEventArgs e)
{
var vms = XmlSerializerContent.ReadAsXmlSerializable<VMs>(
e.Response.Content);
_vmList.DataContext = vms.VM;
}

No meu exemplo, nome do cada VM é realmente um link, porque um cliente pode solicitar dados de uma determinada VM fazendo para o nome como parte do URI. Portanto, http://localhost/HyperVServices/VMs.svc/MyDesktop (onde MyDesktop é o valor do elemento Name no elemento da VM dentro da coleção) é o URI do recurso da VM MyDesktop.

Imagine que esse serviço RESTful permite para provisionamento e iniciar VMs. Faria sentido para incorporar dentro de cada hiperlinks de recurso VM para os vários status que uma VM pode ser colocada em um determinado momento, em vez de permitir o cliente inicie uma VM que não tenha sido configurada corretamente ainda.

Usar HATEOAS ajuda a verificar o estado atual do aplicativo, e também incentiva os clientes mais flexível (já que os clientes não precisam saber muito sobre a estrutura URI do serviço).

Mais fácil que você pense

Não há nenhuma dúvida criação de clientes RESTful é mais difícil iniciar ao fazer isso de criação de clientes baseados em SOAP, WSDL metadados com suporte. Conforme escrevi na minha coluna anterior, "REST é simples, mas simples não significa necessariamente fácil. SOAP é fácil (devido a WSDL), mas fácil nem sempre significa "com práticas recomendadas para codificação e ferramentas como o REST Starter Kit, criando RESTful clientes podem ser mais fácil do que você acha que simples.. E no final Acho que você descobrirá que as vantagens que você obter usando o estilo de arquitetura mais fará para qualquer atraso temporário na criação de clientes.

Jon Flanders é consultor independente, palestrante e instrutor da Pluralsight. Ele é especializado no BizTalk Server, Windows Workflow Foundation e Windows Communication Foundation. Você pode contatar no de masteringbiztalk.com/blogs/jon.