Skip to content

Instantly share code, notes, and snippets.

@alexandreaquiles
Last active April 4, 2022 22:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexandreaquiles/1f1546ff4ccee2e986e7607bf3d07bb3 to your computer and use it in GitHub Desktop.
Save alexandreaquiles/1f1546ff4ccee2e986e7607bf3d07bb3 to your computer and use it in GitHub Desktop.

Integrando sistemas com o protocolo da Web

Vamos implementar essa integração entre sistemas usando o protocolo da Web, o HTTP (Hyper Text Transfer Protocol).

A história do HTTP

Mas da onde vem o HTTP?

No década de 80, (o agora Sir) Tim Berners-Lee trabalhava no CERN, a Organização Europeia para a Pesquisa Nuclear. Em 1989, Berners-Lee criou uma aplicação que provia uma UI para diferentes documentos como relatórios, notas, documentação, etc. Para isso, baseou-se no conceito de hypertext, em que nós de informação são ligados a outros nós formando uma teia em que o usuário pode navegar. E com o nascimento de Internet, a rede mundial de computadores, essa navegação poderia expor informações de diferentes servidores dentro e fora do CERN. Berners-Lee chamou essa teia mundial de documentos ligados uns aos outros de World Wide Web.

Então, a equipe de Tim Berners-Lee criou alguns softwares para essa aplicação:

  • um servidor Web que provia documentos pela Internet
  • um cliente Web, o navegador, que permitia aos usuários visualizar e seguir os links entre os documentos
  • o HTML, um formato para os documentos
  • o HTTP, o protocolo de comunicação entre o navegador e o servidor Web

O protocolo HTTP foi inicialmente especificado, em sua versão 1.0, pela Internet Engineering Task Force (IETF) na RFC 1945 (BERNES-LEE et al., 1996), em um grupo de trabalho liderado por Tim Berners-Lee, Roy Fielding e Henrik Frystyk. Desde então, diversas atualizações foram feitas no protocolo, em diferentes RFCs.

O HTTP é um protocolo do tipo request/response, em que um cliente como um navegador faz uma chamada ao servidor Web e fica aguardando os dados de resposta. Tanto o cliente como o servidor precisam estar no ar ao mesmo tempo para que a chamada seja feita com sucesso. Portanto, podemos dizer que o HTTP é um protocolo síncrono.

HTTP é o protocolo do maior Sistema Distribuído do mundo, a Web, que usada diariamente por bilhões de pessoas. Podemos dizer que é um protocolo bem sucedido!

O HTTP tem algumas ideias interessantes. Vamos estudá-las a seguir.

Recursos

Um recurso é um substantivo, uma coisa que está em um servidor Web e pode ser acessado por diferentes clientes. Pode ser um livro, uma lista de restaurantes, um post em um blog.

Todo recurso tem um endereço, uma URL (Uniform Resource Locator). Por exemplo, a URL dos tópicos mais recentes de Java no fórum da Alura:

https://cursos.alura.com.br/forum/subcategoria-java/todos/1

URL é um conceito da Internet e não só da Web. A especificação inicial foi feita na RFC 1738 (BERNES-LEE et al., 1994) pela IETF. Podemos ter URLs para recursos disponíveis por FTP, SMTP ou AMQP. Por exemplo, uma URL de conexão com o RabbitMQ que usaremos mais adiante:

amqp://eats:caelum123@rabbitmq:5672

Um URI (Uniform Resource Identifier) é uma generalização de URLs que identifica um recurso que não necessariamente está exposto em uma rede. Foi especificado inicialmente pela IETF na RFC 2396 (BERNES-LEE et al., 1998). Por exemplo, um Data URI que representa uma imagem PNG de um pequeno ponto vermelho:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==

Representações

No HTTP, um recurso pode ter diferentes representações. Por exemplo, os dados de um livro da Casa do Código, disponível na URL https://www.casadocodigo.com.br/products/livro-git-github, pode ser representado em XML:

<livro>
  <nome>Controlando versões com Git e GitHub</nome>
  <autores>
    <autor>Alexandre Aquiles</autor>
    <autor>Rodrigo Caneppele</autor>
  </autores>
  <paginas>220</paginas>
  <ISBN>978-85-66250-53-4</ISBN>
</livro>

O mesmo recurso, da URL https://www.casadocodigo.com.br/products/livro-git-github, pode ser representado em JSON:

{
  "nome": "Controlando versões com Git e GitHub",
  "autores": [
    { "autor": "Alexandre Aquiles" },
    { "autor": "Rodrigo Caneppele" }
  ],
  "paginas": 220,
  "ISBN": "978-85-66250-53-4"
}

E é possível ter representações do mesmo recurso, o livro da Casa do Código, em formatos de ebook como PDF, EPUB e MOBI.

As representações de um recurso devem seguir um Media Type. Media Types são padronizados pela Internet Assigned Numbers Authority (IANA), a mesma organização que mantém endereços IP, time zones, os top level domains do DNS, etc.

Curiosidade: os Media Types eram originalmente definidos como MIME (Multipurpose Internet Mail Extensions) Types, em uma especificação que definia o conteúdo de emails e seus anexos.

Entre os Media Types comuns, estão:

  • text/html para HTML
  • text/plain para texto puro (mas cuidado com a codificação!)
  • image/png para imagens no formato PNG
  • application/json, para JSON
  • application/xml para XML
  • application/pdf para PDF
  • application/epub+zip para ebooks EPUB
  • application/vnd.amazon.mobi8-ebook para ebooks MOBI
  • application/vnd.ms-excel para arquivos .xls do Microsoft Excel

Métodos

Para indicar uma ação a ser efetuada em um determinado recurso, o HTTP define os métodos. Se os recursos são os substantivos, os métodos são os verbos, como são comumente chamados.

O HTTP define apenas 9 métodos, cada um com o seu significado e uso diferentes:

  • GET: usado para obter uma representação de um recurso em uma determinada URL.
  • HEAD: usado para obter os metadados (cabeçalhos) de um recurso sem a sua representação. É um GET sem o corpo do response.
  • POST: a representação do recurso passada no request é usada para criar um novo recurso subordinado no servidor, com sua própria URL.
  • PUT: o request contém uma representação do recurso que será utilizada para atualizar ou criar um recurso na URL informada.
  • PATCH: o request contém uma representação parcial de um recurso, que será utilizada para atualizá-lo. É uma adição tardia ao protocolo, especificada na RFC 5789 (DUSSEAULT; SNELL, 2010).
  • DELETE: o recurso da URL informada é removido do servidor.
  • OPTIONS: retorna os métodos HTTP suportados por uma URL.
  • TRACE: repete o request, para que o cliente saiba se há alguma alteração feita por servidores intermediários.
  • CONNECT: transforma o request em um túnel TCP/IP para permitir comunicação encriptada através de um proxy.

Os métodos que não causam efeitos colaterais e cuja intenção é recuperação de dados são classificados como safe. São eles: GET, HEAD, OPTIONS e TRACE. Já os métodos que mudam os recursos ou causam efeitos em sistemas externos, como transações financeiras ou transmissão de emails, não são considerados safe.

Já os métodos em que múltiplos requests idênticos tem o mesmo efeito de apenas um request são classificados de idempotent, podendo ter ou não efeitos colaterais. Todos os métodos safe são idempotentes e também os métodos PUT e DELETE. Os métodos POST e CONNECT não são idempotentes.

O método PATCH não é considerado nem safe nem idempotente pela RFC 5789 (DUSSEAULT; SNELL, 2010), já que parte de um estado específico do recurso no servidor e só contém aquilo que deve ser alterado. Dessa maneira, múltiplos PATCHs podem ter efeitos distintos no servidor, pois o estado do recurso pode ser diferente entre os requests.

A ausência de efeitos colaterais e idempotências dos métodos assim classificados fica a cargo do desenvolvedor, não sendo garantidas pelo protocolo nem pelos servidores Web.

Resumindo:

  • métodos safe: GET, HEAD, OPTIONS e TRACE
  • métodos idempotentes: os métodos safe, PUT e DELETE
  • métodos nem safe nem idempotentes: POST, CONNECT e PATCH

É importante notar que apenas esses 9 métodos, ou até um subconjunto deles, são suficientes para a maioria das aplicações distribuídas. Geralmente são descritos como uma interface uniforme.

Cabeçalhos

Tanto um request como um response HTTP podem ter, além de um corpo, metadados nos Cabeçalhos HTTP. Os cabeçalhos possíveis são especificados por RFCs na IETF e atualizados pela IANA. Alguns dos cabeçalhos mais utilizados:

  • Accept: usado no request para indicar qual representação (Media Type) é aceito no response
  • Access-Control-Allow-Origin: usado no response por chamadas CORS para indicar quais origins podem acessar um recurso
  • Authorization: usado no request para passar credenciais de autenticação
  • Allow: usado no response para indicar os métodos HTTP válidos para o recurso
  • Content-type: a representação (Media Type) usada no request ou no response
  • ETag: usado no response para indicar a versão de um recurso
  • If-None-Match: usado no request com um ETag de um recurso, permitindo caching
  • Location: usado no response para indicar uma URL de redirecionamento ou o endereço de um novo recurso

Os cabeçalhos HTTP Accept e Content-type permitem a Content negotiation (negociação de conteúdo), em que um cliente pode negociar com um servidor Web representações aceitáveis.

Por exemplo, um cliente pode indicar no request que aceita JSON e XML como representações, com seguinte cabeçalho:

Accept: application/json, application/xml

O servidor Web pode escolher entre essas duas representações. Se entre os formatos suportados pelo servidor não estiver JSON mas apenas XML, o response teria o cabeçalho:

Content-type: application/xml

No corpo do response, estaria um XML representado os dados do recurso.

Um navegador sempre usa o cabeçalho Accept em seus requests. Por exemplo, no Mozilla Firefox:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

O cabeçalho anterior indica que o navegador Mozilla Firefox aceita do servidor as representações HTML, XHTML ou XML, nessa ordem. Se nenhuma dessas estiver disponível, pode ser enviada qualquer representação, indicada pelo */*.

O parâmetro q utilizado no cabeçalho anterior é um relative quality factor, ou fator relativo de qualidade, que indica a preferência por uma representação. O valor varia entre 0, indicando menor preferência, e 1, o valor padrão que indica uma maior preferência. No cabeçalho Accept anterior, o HTML e XHTML tem valor 1, XML tem valor 0.9 e qualquer outra representação com valor 0.8.

Códigos de Status

Um response HTTP pode ter diferentes códigos de status, especificados em RFCs da IETF e mantidos pela IANA.

O primeiro dígito indica a categoria do response:

  • 1XX (Informational): o request foi recebido e o processamento continua
  • 2XX (Success): o request foi recebido, entendido e aceito com sucesso
  • 3XX (Redirection): mais ações são necessárias para completar o request
  • 4XX (Client Error): o request é inválido e contém erros causados pelo cliente
  • 5XX (Server Error): o servidor falhou em completar um request válido

Alguns dos códigos de status mais comuns:

  • 101 Switching Protocols: o cliente solicitou a troca de protocolos e o servidor aceitou, trocando para o protocolo indicado no cabeçalho Upgrade. Usado para iniciar uma conexão a um WebSocket.

  • 200 OK: código padrão de sucesso.

  • 201 Created: indica que um novo recurso foi criado. Em geral, o response contém a URL do novo recurso no cabeçalho Location.

  • 202 Accepted: o request foi aceito para processamento mas ainda não foi completado.

  • 204 No Content: o request foi processado com sucesso mas não há corpo no response.

  • 301 Moved Permanently: todos os requests futuros devem ser redirecionados para a URL indicada no cabeçalho Location.

  • 302 Found: o cliente deve redirecionar para a URL indicada no cabeçalho Location. Navegadores, frameworks e aplicações a implementam como um redirect, que seria o intuito do código 303.

  • 303 See Other: o request foi completado com sucesso mas o response deve ser encontrado na URL indicada no cabeçalho Location por meio de um GET. O intuito era ser utilizado para um redirect, de maneira a implementar o pattern POST/redirect/GET. Criado a partir do HTTP 1.1.

  • 304 Not Modified: indica que a versão (ETag) do recurso não foi modificada em relação à informada no cabeçalho If-None-Match do request. Portanto, não há a necessidade de transmitir uma representação do recurso. O cliente tem a última versão em seu cache.

  • 400 Bad Request: o cliente enviou um request inválido.

  • 401 Unauthorized: o cliente tentou acessar um recurso protegido em que não tem as permissões necessárias. O request pode ser refeito se passado um cabeçalho Authorization que contenha credenciais de um usuário com permissão para acessar o recurso.

  • 403 Forbidden: o cliente tentou uma ação proibida ou o usuário indicado no cabeçalho Authorization não tem acesso ao recurso solicitado.

  • 404 Not Found: o recurso não existe no servidor.

  • 405 Method Not Allowed: o cliente usou um método HTTP não suportado pelo recurso solicitado.

  • 406 Not Acceptable: o servidor não consegue gerar uma representação compatível com nenhum valor do cabeçalho Accept do request.

  • 409 Conflict: o request não pode ser processado por causa de um conflito no estado atual do recurso, como múltiplas atualizações simultâneas.

  • 415 Unsupported Media Type: o request foi enviado com uma representação, indicada no Content-type, não suportada pelo servidor.

  • 429 Too Many Requests: o cliente enviou requests excessivos em uma determinada fatia de tempo. Usado ao implementar rate limiting com o intuito de previnir contra ataques Denial of Service (DoS).

  • 500 Internal Server Error: um erro inesperado aconteceu no servidor. O erro do "Bad, bad server. No donut for your. do Orkut e da baleia do Twitter.

  • 503 Service Unavailable: servidor em manutenção ou sobrecarregado temporariamente.

  • 504 Gateway Timeout: o servidor está servindo como um proxy para um request mas não recebeu a tempo um response do servidor de destino.

Links

A Web tem esse nome por ser uma teia de documentos ligados entre si. Links, ou hypertext, são conceitos muito importantes na Web e podem ser usado na integração de sistemas. Veremos como mais adiante.

REST, o estilo arquitetural da Web

Roy Fielding, um dos autores das especificações do protocolo HTTP e cofundador do Apache HTTP Server, estudou diferentes estilos arquiteturais de Sistemas Distribuídos em sua tese de PhD: Architectural Styles and the Design of Network-based Software Architectures (FIELDING, 2000).

As restrições e princípios que fundamentam o estilo arquitetural da Web são descrita por Fielding da seguinte maneira:

  • Cliente/Servidor: a UI é separada do armazenamento dos dados, permitindo a portabilidade para diferentes plataformas e simplificando o Servidor.
  • Stateless: cada request do cliente deve conter todos os dados necessários, sem tomar vantagem de nenhum contexto armazenado no servidor. Sessões de usuário devem ser mantidas no cliente. Essa característica melhor: a Escalabilidade, já que não há uso de recursos entre requests diferentes; Confiabilidade, já que torna mais fácil a recuperação de falhas parciais; Visibilidade, já que não há a necessidade de monitorar além de um único request. Como desvantagem, há uma piora na Performance da rede, um aumento de dados repetitivos entre requests e uma dependência da implementação correta dos múltiplos clientes.
  • Cache: os requests podem ser classificados como cacheáveis, fazendo com que o cliente possa reusar o response para requests equivalentes. Assim, a Latência é reduzida de maneira a melhorar a Eficiência, Escalabilidade e a Performance percebida pelo usuário. Porém, a Confiabilidade pode ser afetada caso haja aumento significante de dados desatualizados.
  • Interface Uniforme: URLs, representações, métodos padronizados e links são restrições da Web que simplificam a Arquitetura e aumentam a Visibilidade das interações, encorajando a evolução independente de cada parte. Por outro lado, são menos eficientes que protocolos específicos.
  • Sistema em Camadas: cada componente só conhece a camada com a qual interage imediatamente, minimizando a complexidade, aumentando a independência e permitindo intermediários como load balancers, firewalls e caches. Porém, pode ser adicionada latência.
  • Code-On-Demand: os clientes podem estender as funcionalidades por meio da execução de applets e scripts, aumentando a Extensibilidade. Por outro lado, a Visibilidade do sistema diminui.

Fielding chama esse estilo arquitetural da Web de Representational State Transfer, ou simplesmente REST. Adicionando o sufixo -ful, que denota "que possui a característica de" em inglês, podemos chamar serviços que seguem esse estilo arquitetural de RESTful.

Um excelente resumo de boas práticas e princípios de uma API RESTful podem ser encontrado no blog da Caelum, no post REST: Princípios e boas práticas (FERREIRA, 2017), disponível em: https://blog.caelum.com.br/rest-principios-e-boas-praticas

Leonard Richardson e Sam Ruby, no livro RESTful Web Services (RICHARDSON; RUBY, 2007), contrastam serviços no estilo Remote Procedure Call (RPC) com serviços Resource-Oriented.

Um Web Service no estilo RPC expõe operações de uma aplicação. Não há recursos, representações nem métodos. O SOAP é um exemplo de um protocolo nesse estilo RPC: há apenas um recurso com só uma URL (a do Web Service), há apenas uma representação (XML), há apenas um método (POST, em geral). Cada operation do Web Service SOAP é exposta num WSDL.

Richardson e Ruby mostram, no livro, uma API de busca do Google implementada com SOAP. Para buscar sobre "REST" deveríamos efetuar o seguinte request:

POST http://api.google.com/search/beta2
Content-Type: application/soap+xml
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <gs:doGoogleSearch xmlns:gs="urn:GoogleSearch">
      <q>REST</q>
      ...
    </gs:doGoogleSearch>
  </soap:Body>
</soap:Envelope>

A mesma consulta pode ser feita pela API mais moderna do Google, que expõe um recurso search que recebe um parâmetro q com o termo a ser consultado:

GET http://www.google.com/search?q=REST
Content-Type: text/html

O Modelo de Maturidade de Richardson

No post Richardson Maturity Model (FOWLER, 2010), Martin Fowler explica uma heurística para a maturidade da adoção de REST descrita por Leonard Richardson no "ato 3" de sua palestra Justice Will Take Us Millions Of Intricate Moves (RICHARDSON, 2008).

O Modelo de Maturidade descrito por Richardson indica uma progressão na adoção das tecnologias da Web.

Nível 0 - O Pântano do POX

No Nível 0 de Maturidade, o HTTP é usado apenas como um mecanismo de transporte para interações remotas no estilo RPC, sem a filosofia da Web.

Fowler usa o termo Plain Old XML (POX) para descrever APIs que provêem só um endpoint, todas as chamadas usam POST, a única representação é XML. As mensagens de erro contém um status code de sucesso (200) com os detalhes do erro no corpo do response. É o caso de tecnologias como SOAP e XML-RPC.

É importante ressaltar que o uso de XML é apenas um exemplo. Poderiam ser utilizados JSON, YAML ou qualquer outra representação. Esse nível de maturidade trata de uma única representação.

Para ilustrar esse tipo de API, Fowler usa um exemplo de consultas médicas. Por exemplo, para agendar uma consulta:

POST /agendamentoService HTTP/1.1
<horariosDisponiveisRequest data="2010-01-04" doutor="huberman"/>

O retorno seria algo como:

HTTP/1.1 200 OK
<listaDeHorariosDisponiveis>
  <horario inicio="14:00" fim="14:50">
    <doutor id="huberman"/>
  </horario>
  <horario inicio="16:00" fim="16:50">
    <doutor id="huberman"/>
  </horario>
</listaDeHorariosDisponiveis>

Para marcar uma consulta:

POST /agendamentoService HTTP/1.1
<agendamentoConsultaRequest>
  <horario doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
</agendamentoConsultaRequest>

A resposta de sucesso, com a confirmação da consulta, seria algo como:

HTTP/1.1 200 OK
<consulta>
  <horario doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
</consulta>

Em caso de erro, teríamos uma resposta ainda com o código de status 200, mas com os detalhes do erro no corpo do response:

HTTP/1.1 200 OK
<agendamentoConsultaRequestFailure>
  <horario doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
  <motivo>Horário não disponível</motivo>
</agendamentoConsultaRequestFailure>

Perceba que, no exemplo de Fowler, não foi utilizado SOAP mas uma API que usa HTTP, só que sem os conceitos do protocolo.

Nível 1 - Recursos

Um primeiro passo, o Nível 1 de Maturidade, é ter diferentes recursos, cada um com sua URL, ao invés de um único endpoint para toda a API.

No exemplo de consultas médicas de Fowler, poderíamos ter um recurso específico para um doutor:

POST /doutores/huberman HTTP/1.1
<horariosDisponiveisRequest data="2010-01-04"/>

O response seria semelhante ao anterior, mas com uma maneira de endereçar individualmente cada horário disponível para um doutor específico:

HTTP/1.1 200 OK
<listaDeHorariosDisponiveis>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50"/>
  <horario id="5678" doutor="huberman" inicio="16:00" fim="16:50"/>
</listaDeHorariosDisponiveis>

Com um endereço para cada horário, marcar uma consulta seria fazer um POST para um recurso específico:

POST /horarios/1234 HTTP/1.1
<agendamentoConsulta>
  <paciente id="alexandre"/>
</agendamentoConsulta>

O response seria semelhante ao anterior:

HTTP/1.1 200 OK
<consulta>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
</consulta>

Nível 2 - Verbos HTTP

O segundo passo, o Nível 2 de Maturidade, é utilizar os verbos (ou métodos) HTTP o mais perto o possível de seu intuito original.

Para obter a lista de horários disponíveis, poderíamos usar um GET:

GET /doutores/huberman/horarios?data=2010-01-04&status=disponivel HTTP/1.1

A resposta seria a mesma de antes:

HTTP/1.1 200 OK
<listaDeHorariosDisponiveis>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50"/>
  <horario id="5678" doutor="huberman" inicio="16:00" fim="16:50"/>
</listaDeHorariosDisponiveis>

No Nível 2 de Maturidade, Fowler diz que o uso de GET para consultas é crucial. Como o HTTP define o GET como uma operação safe, isso significa que não há mudanças significativas no estado de nenhum dos dados. Assim, é seguro invocar um GET diversas vezes seguidas e obter os mesmos resultados. Uma consequência importante é que é possível fazer cache dos resultados, melhorando a Performance.

Para marcar uma consulta, é necessário um verbo HTTP que permite a mudança de estado. Poderíamos usar um POST, da mesma maneira anterior:

POST /horarios/1234 HTTP/1.1
<agendamentoConsulta>
  <paciente id="alexandre"/>
</agendamentoConsulta>

Uma API de Nível 2 de Maturidade, deve usar os códigos de status e cabeçalhos a seu favor. Para indicar que uma nova consulta foi criada, com o agendamento do paciente naquele horário podemos usar o status 201 Created. Esse status deve incluir, no response, um cabeçalho Location com a URL do novo recurso. Essa nova URL pode ser usada pelo cliente para obter, com um GET, mais detalhes sobre o recurso que acabou de ser criado. Portanto, a resposta teria alguns detalhes diferentes da anterior:

HTTP/1.1 201 Created
Location: horarios/1234/consulta
<consulta>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
</consulta>

No caso de um erro, devem ser usados códigos 4XX ou 5XX. Por exemplo, para indicar que houve uma atualização do recurso por outro cliente, pode ser usado o status 409 Conflict com uma nova lista de horários disponíveis no corpo do response:

HTTP/1.1 409 Conflict
<listaDeHorariosDisponiveis>
  <horario id="5678" doutor="huberman" inicio="16:00" fim="16:50"/>
</listaDeHorariosDisponiveis>

Nível 3 - Controles de Hypermedia

Um dos conceitos importantes do HTTP, o protocolo da Web, é o uso de hypertext.

O Nível 3, o nível final, de Maturidade de uma API RESTful é atingido quando são utilizados links. Mas falaremos sobre isso mais adiante.

O poder dos Links

Ao descrevermos as boas ideias do procolo HTTP, mencionamos a importância dos links. Quando falamos sobre REST e sobre o Nível 3, o máximo, do Modelo de Maturidade de Leonard Richardson, falamos mais uma vez de links.

Roy Fielding, o criador do termo REST, diz no post REST APIs must be hypertext-driven (FIELDING, 2008) que toda API, para ser considerada RESTful, deve necessariamente usar links.

Hypertext? Hypermedia?

HyperText é um conceito tão importante para a Web que está nas iniciais de seu protocolo, o HTTP, e de seu formato de documentos original, o HTML.

O termo hypertext, foi cunhado por Ted Nelson e publicado no artigo Complex Information Processing (NELSON, 1965) para denotar um material escrito ou pictórico interconectado de maneira tão complexa que não pode ser representado convenientemente em papel. A ideia original é que seria algo que iria além do texto e deveria ser representado em uma tela interativa.

Nos comentários do post REST APIs must be hypertext-driven (FIELDING, 2008), Fielding dá a sua definição:

Quando eu digo hypertext, quero dizer a apresentação simultânea de informações e controles, de forma que as informações se tornem o meio pelo qual o usuário (ou autômato) obtém escolhas e seleciona ações.

A hypermedia é apenas uma expansão sobre o que o texto significa para incluir âncoras temporais em um fluxo de mídia; a maioria dos pesquisadores abandonou a distinção.

Hypertext não precisa ser HTML num navegador. Máquinas podem seguir links quando entendem o formato de dados e os tipos de relacionamento.

Monte a sua história seguindo links

No final da década de 1970 e começo da década de 1980, surgiu a série de livros-jogo "Escolha a sua aventura" (em inglês, Choose Your Own Adventure), lançada pela editora americana Bantam Books e editada pela Ediouro no Brasil.

Títulos como "A Gruta do Tempo" (em inglês, The Cave of Time), de Edward Packard, permitiam que o leitor fizesse escolhas e determinasse o rumo da história.

Na página 1 do livro, a história era contada linearmente, até que o personagem chegava a um ponto de decisão, em que eram oferecidas algumas opções para o leitor determinar o rumo da narrativa. Por exemplo:

  • Se quiser seguir o velho camponês para ver aonde ele vai, vá para a página 3.
  • Se preferir voltar para casa, vá para a página 15.
  • Se quiser sentar e esperar, vá para a página 71.

O leitor poderia coletar itens no decorrer da história que seriam determinadas em trechos posteriores. Por exemplo:

  • Se você tiver o item mágico "Olho do Falcão", vá para a página 210.
  • Se você não tiver o item, mas possui a perícia "Rastreamento", vá para a página 19.
  • Caso contrário, vá para a página 101.

Essa ideia de montar a sua história seguindo links parece muito com o uso de hypertext para APIs RESTful.

Em sua palestra Getting Things Done with REST (ROBINSON, 2011), Ian Robinson cita talvez o mais famoso desses livros-jogo, o de título "O Feiticeiro da Montanha de Fogo" (em inglês, The Warlock of Firetop Mountain), escrito por Ian Livingstone e Steve Jackson e publicado em 1982 pela Puffin Books.

Revisitando o Modelo de Maturidade de Richardson

Quando falamos sobre o Nível 3, o máximo, do Modelo de Maturidade de Leonard Richardson descrito por Martin Fowler no post Richardson Maturity Model (FOWLER, 2010), apenas mencionamos que links são importantes.

Agora, sabemos que links podem ser usados para descrever a transição de estados da aplicação, o que chamamos de HATEOAS.

Voltando ao exemplo de consultas médicas, cada horário da lista de horários disponíveis pode conter um link de agendamento:

GET /doutores/huberman/horarios?data=2010-01-04&status=disponivel HTTP/1.1

A resposta seria a mesma de antes:

HTTP/1.1 200 OK
<listaDeHorariosDisponiveis>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50">
    <link rel="agendamento" href="/horarios/1234">
  </horario>
  <horario id="5678" doutor="huberman" inicio="16:00" fim="16:50">
    <link rel="agendamento" href="/horarios/5678">
  </horario>
</listaDeHorariosDisponiveis>

Os links descrevem o que pode ser feito a seguir e as URLs que precisam ser manipuladas.

Seguindo o link de agendamento, o POST para marcar uma consulta seria feito da mesma maneira anterior:

POST /horarios/1234 HTTP/1.1
<agendamentoConsulta>
  <paciente id="alexandre"/>
</agendamentoConsulta>

O resultado poderia conter links para diferentes possibilidades:

HTTP/1.1 201 Created
Location: horarios/1234/consulta
<consulta>
  <horario id="1234" doutor="huberman" inicio="14:00" fim="14:50"/>
  <paciente id="alexandre"/>
  <link rel="agendamentoExame" href="/horarios/1234/consulta/exames">
  <link rel="cancelamento" href="/horarios/1234/consulta">
  <link rel="mudancaHorario" href="/doutor/huberman/horarios?data=2010-01-04&status=disponivel">
</consulta>

Assim, as URLs podem ser modificadas sem que o código dos clientes quebre. Um benefício adicional é que os clientes podem conhecer e explorar os próximos passos.

Idealmente, um cliente deveria depender apenas da URL raiz de uma API e, a partir dela, navegar pelos links, descobrindo o que pode ser feito com a API.

Fowler menciona a ideia de Ian Robinson de que o Modelo de Maturidade de Richardson está relacionado com técnicas comuns de design:

  • O Nível 1 trata de como lidar com a complexidade usando "dividir e conquistar", dividindo um grande endpoint para o serviço todo em vários recursos.
  • O Nível 2 adiciona os métodos HTTP como um padrão, de maneira que possamos lidar com situações semelhantes da mesma maneira, evitando variações desnecessárias.
  • O Nível 3 introduz a capacidade de descoberta (em inglês, Discoverability), fornecendo uma maneira de tornar o protocolo auto-documentado.

HTTP/2

Nada impede que formatos binários de serialização de dados sejam usados com um transporte HTTP. Mas o protocolo HTTP/1.1 em si é ineficiente por ser baseado em texto, com diversos cabeçalhos e uma conexão TCP a cada request/response.

Ilya Grigorik, no livro High Performance Browser Networking (GRIGORIK, 2013), descreve o trabalho de desenvolvedores do Google em um protocolo experimental com o objetivo de reduzir em 50% o tempo de carregamento de páginas. O resultado desse experimento foi o protocolo SPDY, que foi progressivamente adotado por diferentes empresas.

Grigorik relata que, em 2012, o HTTP Working Group da IETF começou a trabalhar num draft inspirado no SPDY. Em 2015, foi publicada a RFC 7540 (BELSHE et al., 2015), que especifica o HTTP/2.

O protocolo HTTP/2 aproveita melhor as conexões TCP sobre as quais é construído, codificando e comprimindo os dados em frames binários, que contém os cabeçalhos separados dos dados. Há ainda a possibilidade de streams múltiplas, iniciadas pelo cliente ou pelo servidor.

Grigorik descreve a terminologia do HTTP/2:

  • Stream: um fluxo bidirecional de bytes em uma mesma conexão, que pode transportar uma ou mais mensagens.
  • Mensagem: uma sequência completa de frames que equivalem a um request ou response.
  • Frame: a menor unidade de comunicação do HTTP/2, transporta um tipo específico de dados, como cabeçalhos HTTP, o payload, etc. Cada frame tem no mínimo um cabeçalho que identifica a qual stream pertence.

No HTTP/1.x, era possível realizar múltiplos requests paralelos, cada um em uma conexão TCP diferente, mas apenas um response poderia ser entregue por vez. Com o HTTP/2, é possível, na mesma conexão TCP, obter frames independentes intercalados que são remontados do lado de quem recebe os dados, no cliente ou no servidor. Isso é chamado de multiplexing.

Um servidor HTTP/2 pode fazer um server push, enviando múltiplos frames em resposta a um mesmo request do cliente. Dessa maneira, como resposta a um request de um HTML, o servidor poderia enviar, além do HTML, todos os JS e CSS associados em diferentes frames. Assim, não há a necessidade de concatenação, como havia no HTTP/1.x.

No livro, Grigorik diz que, apesar do HTTP/2 não requerer o uso de conexões seguras, TLS é o mecanismo mais confiável e eficiente para HTTP/2, eliminando a necessidade de latência ou roundtrips extras. Além disso, os navegadores adicionam a restrição de só habilitar HTTP/2 sobre uma conexão TLS.

Os recursos, URLs, métodos, cabeçalhos e códigos de status são mantidos no HTTP/2. Porém, como há uma nova codificação binária, tanto o servidor como o cliente precisam ser compatíveis com o HTTP/2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment