API REST - por que usar PUT DELETE POST GET?


155

Então, eu estava olhando alguns artigos sobre a criação de APIs REST. E alguns deles sugerem o uso de todos os tipos de solicitações HTTP: como PUT DELETE POST GET. Nós criaríamos, por exemplo, index.php e escreveríamos a API desta maneira:

$method = $_SERVER['REQUEST_METHOD'];
$request = split("/", substr(@$_SERVER['PATH_INFO'], 1));

switch ($method) {
  case 'PUT':
    ....some put action.... 
    break;
  case 'POST':
    ....some post action.... 
    break;
  case 'GET':
    ....some get action.... 
    break;
  case 'DELETE':
    ....some delete action.... 
    break;
}

OK, garantido - ainda não sei muito sobre serviços da web. Mas, não seria mais fácil aceitar o objeto JSON por meio de regular POSTou GET(que conteria o nome do método e todos os parâmetros) e, em seguida, responderia em JSON também. Podemos facilmente serialize / deserialize via PHP de json_encode()e json_decode()e fazer o que quisermos com esses dados sem ter que lidar com diferentes métodos de solicitação HTTP.

Estou esquecendo de algo?

ATUALIZAÇÃO 1:

Ok - depois de pesquisar várias APIs e aprender muito sobre XML-RPC , JSON-RPC , SOAP , REST , concluí que esse tipo de API é válido. Na verdade, o intercâmbio de pilhas está usando essa abordagem em seus sites e acho que essas pessoas sabem o que estão fazendo na API do Stack Exchange .


4
Por que forçar uma carga útil JSON? E se não houver JSON, e é um GET antigo simples?
Mike DeSimone 01/01

Respostas:


200

A idéia de RE apresentação S tate T ransferência não é sobre o acesso a dados da forma mais simples possível.

Você sugeriu o uso de solicitações de postagem para acessar o JSON, que é uma maneira perfeitamente válida de acessar / manipular dados.

O REST é uma metodologia para acesso significativo aos dados. Quando você vê uma solicitação no REST, deve ser aparente imediatamente o que está acontecendo com os dados.

Por exemplo:

GET: /cars/make/chevrolet

provavelmente retornará uma lista de carros chevy. Uma boa API REST pode até incorporar algumas opções de saída na string de consulta como ?output=jsonou ?output=htmlque permitiriam ao acessador decidir em qual formato a informação deveria ser codificada.

Após um pouco de pensamento sobre como digitação de dados razoavelmente incorporar em uma API REST, cheguei à conclusão que a melhor maneira de especificar o tipo de dados explicitamente seria através da extensão de arquivo já existente, como .js, .json, .html, ou .xml. Uma extensão de arquivo ausente será padronizada para qualquer formato padrão (como JSON); uma extensão de arquivo não suportada pode retornar um 501 Not Implementedcódigo de status .

Outro exemplo:

POST: /cars/
{ make:chevrolet, model:malibu, colors:[red, green, blue, grey] }

provavelmente criará um novo chevy malibu no banco de dados com as cores associadas. Eu digo que provavelmente como a API REST não precisa estar diretamente relacionada à estrutura do banco de dados. É apenas uma interface de máscara para proteger os dados verdadeiros (pense nisso como acessadores e mutadores para uma estrutura de banco de dados).

Agora precisamos avançar para a questão da idempotência . Normalmente, o REST implementa o CRUD sobre HTTP. HTTP usa GET, PUT, POSTe DELETEpara as solicitações.

Uma implementação muito simplista do REST poderia usar o seguinte mapeamento CRUD:

Create -> Post
Read   -> Get
Update -> Put
Delete -> Delete

Há um problema com esta implementação: Post é definido como um método não idempotente. Isso significa que chamadas subseqüentes do mesmo método Post resultarão em diferentes estados do servidor. Get, Put e Delete são idempotentes; o que significa que chamá-los várias vezes deve resultar em um estado de servidor idêntico.

Isso significa que uma solicitação como:

Delete: /cars/oldest

poderia realmente ser implementado como:

Post: /cars/oldest?action=delete

Enquanto que

Delete: /cars/id/123456

resultará no mesmo estado do servidor se você chamá-lo uma vez ou se você chamar 1000 vezes.

Uma maneira melhor de lidar com a remoção do oldestitem seria solicitar:

Get: /cars/oldest

e use os IDdados resultantes para fazer uma deletesolicitação:

Delete: /cars/id/[oldest id]

Um problema com esse método seria se outro /carsitem fosse adicionado entre quando /oldestfoi solicitado e quando deletefoi emitido.


3
@ Além disso, é uma combinação de várias razões: Seguir as diretrizes HTTP significa que você (provavelmente) terá menos problemas de compatibilidade com versões anteriores quando as coisas mudam; o uso de um formulário html via POST alertará o usuário sobre o envio múltiplo dos mesmos dados (isso evita uma transação não idempotente); seguir uma melhor prática bem definida é, bem ... a melhor prática. O descanso não é definido com uma implementação específica em mente, que permite usá-lo como achar melhor. Eu sugiro aproveitando todos de HTTP códigos de erro e métodos de solicitação, mas você está autorizado a fazê-lo como quiser
zzzzBov

4
Portanto, o problema com esta resposta (é uma resposta decente, mas não completa) é que ela não aborda a pergunta principal que ele fez: Por que você usaria verbos HTTP e o URI em vez de dados JSON personalizados (talvez algum tipo de Sintaxe de chamada de API baseada em JSON). Você pode criar sua sintaxe JSON customizada para que seja "imediatamente ... aparente o que está acontecendo com os dados". O que você não pode fazer é usar facilmente recursos internos e camadas de rede sobre HTTP, como você pode com uma API que segue todas as convenções REST. Não que a minha resposta é perfeito, é claro;)
Merlyn Morgan-Graham

4
@Andre: Os exemplos que a entrada do wiki usa são autenticação, armazenamento em cache e negociação de tipo de conteúdo. Agora que estou pensando mais sobre isso, talvez você possa usá-las com interfaces de estilo RPC, mas a tentação geralmente será implementar seu próprio sistema a partir do zero ou codificar uma integração a um sistema existente. Com o REST, você pode usar a integração embutida e administrá-la no servidor da web. Isso significa um acoplamento mais flexível, o que significa que você precisa implementar menos e seu aplicativo é muito mais flexível para alterar opções no futuro, com menor impacto no código e no teste.
Merlyn Morgan-Graham

10
Em vez de DELETE: / cars / older, que tal GET: / cars / older seguido de um DELETE? Dessa forma, você possui dois comandos idempotentes separadamente.
Neil

3
+1; Eu concordo que esta é uma boa resposta (estou analisando novamente por diversão e lucro). POST: /cars/oldestser um substituto para um DELETE não faz muito sentido. Algo como - POST: /cars/oldest/deletepode, acho que gosto mais da solução de Neil. A única vantagem que uma exclusão direta oferece sobre sua solução get-id-delete-id é a atomicidade. Eu gostaria de uma justificativa comercial clara com um cenário não artificial antes de implementar uma coisa dessas. Você não precisa suportar todos os verbos em todos os objetos / URLs.
Merlyn Morgan-Graham

39

Esta é uma questão de segurança e manutenção.

métodos seguros

Sempre que possível, você deve usar métodos 'seguros' (unidirecionais), como GET e HEAD, para limitar a vulnerabilidade em potencial.

métodos idempotentes

Sempre que possível, você deve usar métodos 'idempotentes', como GET, HEAD, PUT e DELETE, que não podem ter efeitos colaterais e, portanto, são menos propensos a erros / mais fáceis de controlar.

Fonte


1
Desculpe, mas como são os métodos idempotentes PUT e DELETE? Eles afetam o estado do servidor e seus dados!
Mahmoud Al-Qudsi 01/01

27
@ Computador: Fazer o mesmo PUT ou o mesmo DELETE resulta no mesmo estado final. É isso que significa "idempotente".
Ignacio Vazquez-Abrams

4
Para mais esclarecimentos: uma operação F é idempotente, se sua aplicação única e suas diversas aplicações conseqüentes retornarem o mesmo resultado. Mais precisamente, F é idempotente se e somente se F (x) = F (F (x)). Por exemplo, Excluir é idempotente, porque quando você exclui um item uma vez ou o exclui várias vezes, o resultado é o mesmo: o item é excluído apenas uma vez com o primeiro aplicativo de exclusão e nada acontece no segundo ou terceiro aplicativo de exclusão.
Qartal

1
Mas em termos de criação, quando você cria um novo registro com um comando create e emite o mesmo comando novamente, dois registros (provavelmente) são criados (embora ambos reflitam a mesma informação).
Qartal

qartal - sua definição funcional para idempotente deve ser 'F (X) = F (X) F (X)'. Ótima maneira de expressá-lo embora.
precisa

26

Em suma, o REST enfatiza substantivos sobre verbos. À medida que sua API se torna mais complexa, você adiciona mais coisas, em vez de mais comandos.


2
Tive um pouco de dificuldade para entender isso. Este post ( lornajane.net/posts/2013/... ) que o verbo deve vir a partir da solicitação HTTP para que o URI deve então conter apenas substantivos limpou-se um pouco para mim
icc97

9

Você perguntou :

não seria mais fácil aceitar o objeto JSON por meio de $ _POST normal e responder em JSON também

Da Wikipedia em REST :

Os aplicativos RESTful maximizam o uso da interface pré-existente e bem definida e outros recursos internos fornecidos pelo protocolo de rede escolhido e minimizam a adição de novos recursos específicos do aplicativo.

Pelo que (pouco) eu vi, acredito que isso geralmente é realizado maximizando o uso de verbos HTTP existentes e criando um esquema de URL para o seu serviço que seja o mais poderoso e evidente possível.

Os protocolos de dados personalizados (mesmo que sejam criados sobre os padrões, como SOAP ou JSON) são desencorajados e devem ser minimizados para melhor se adaptar à ideologia REST.

O RAP SOAP sobre HTTP, por outro lado, incentiva cada designer de aplicativo a definir um vocabulário novo e arbitrário de substantivos e verbos (por exemplo, getUsers (), savePurchaseOrder (...)), geralmente sobrepostos ao verbo HTTP 'POST'. Isso desconsidera muitos dos recursos existentes do HTTP, como autenticação, armazenamento em cache e negociação de tipo de conteúdo, e pode deixar o designer do aplicativo reinventando muitos desses recursos no novo vocabulário.

Os objetos reais com os quais você está trabalhando podem estar em qualquer formato. A idéia é reutilizar o máximo de HTTP possível para expor suas operações que o usuário deseja executar nesses recursos (consultas, gerenciamento / mutação de estado, exclusão).

Você perguntou :

Estou esquecendo de algo?

Há muito mais a saber sobre o REST e os próprios verbos de sintaxe / HTTP do URI. Por exemplo, alguns dos verbos são idempotentes, outros não. Eu não vi nada sobre isso na sua pergunta, então não me incomodei em tentar mergulhar nela. As outras respostas e a Wikipedia têm muitas informações boas.

Além disso, há muito o que aprender sobre as várias tecnologias de rede criadas sobre HTTP, das quais você pode tirar proveito se estiver usando uma API realmente tranquila. Eu começaria com autenticação.


8

No que diz respeito ao uso da extensão para definir o tipo de dados. Notei que a API do MailChimp está fazendo isso, mas não acho que seja uma boa ideia.

GET /zzz/cars.json/1

GET /zzz/cars.xml/1

Parece uma boa ideia, mas acho que a abordagem "mais antiga" é melhor - usando cabeçalhos HTTP

GET /xxx/cars/1
Accept: application/json

Além disso, os cabeçalhos HTTP são muito melhores para comunicação entre tipos de dados (se alguém precisar)

POST /zzz/cars
Content-Type: application/xml     <--- indicates we sent XML to server
Accept: application/json          <--- indicates we want get data back in JSON format  

4

Estou esquecendo de algo?

Sim. ;-)

Esse fenômeno existe devido à restrição de interface uniforme . O REST gosta de usar padrões já existentes em vez de reinventar a roda. O padrão HTTP já provou ser altamente escalável (a web está funcionando por um tempo). Por que devemos consertar algo que não está quebrado ?!

nota: A restrição de interface uniforme é importante se você deseja dissociar os clientes do serviço. É semelhante à definição de interfaces para classes, a fim de separá-las uma da outra. Claro. aqui a interface uniforme consiste em padrões como HTTP , tipos MIME , URI , RDF , vocabs de dados vinculados , vocabulário hidra , etc.


2

A boa semântica é importante na programação.

A utilização de mais métodos além do GET / POST será útil, pois aumentará a legibilidade do seu código e facilitará a manutenção.

Por quê?

Porque você sabe que o GET recuperará dados da sua API. Você sabe que o POST adicionará novos dados ao seu sistema. Você sabe que o PUT fará atualizações. DELETE excluirá linhas etc, etc,

Normalmente, estruturo meus Serviços Web RESTFUL para que eu tenha um retorno de chamada de função nomeado da mesma maneira que o método.

Eu uso PHP, então eu uso function_exists (eu acho que é chamado). Se a função não existir, eu jogo um 405 (MÉTODO NÃO PERMITIDO).


2

Bill Venners: Em sua postagem no blog intitulada "Por que o REST falhou", você disse que precisamos dos quatro verbos HTTP - GET, POST, PUT e DELETE - e lamentou que os fornecedores de navegadores apenas GET e POST. "Por que precisamos dos quatro Por que GET e POST não são suficientes?

Elliotte Rusty Harold: Existem quatro métodos básicos no HTTP: GET, POST, PUT e DELETE. GET é usado na maioria das vezes. É usado para qualquer coisa segura, que não cause efeitos colaterais. O GET pode ser marcado, armazenado em cache, vinculado a, passado por um servidor proxy. É uma operação muito poderosa, uma operação muito útil.

O POST, por outro lado, é talvez a operação mais poderosa. Pode fazer qualquer coisa. Não há limites para o que pode acontecer e, como resultado, você deve ter muito cuidado com isso. Você não adiciona aos favoritos. Você não o armazena em cache. Você não a busca previamente. Você não faz nada com um POST sem perguntar ao usuário. Você quer fazer isso? Se o usuário pressionar o botão, você poderá POSTAR algum conteúdo. Mas você não verá todos os botões de uma página e começará a pressioná-los aleatoriamente. Por outro lado, os navegadores podem olhar para todos os links da página e buscá-los previamente, ou aqueles que eles acham que provavelmente serão seguidos a seguir. De fato, alguns navegadores, extensões do Firefox e várias outras ferramentas tentaram fazer isso em um ponto ou outro.

PUT e DELETE estão no meio entre GET e POST. A diferença entre PUT ou DELETE e POST é que PUT e DELETE são * idempotentes, enquanto POST não é. PUT e DELETE podem ser repetidos, se necessário. Digamos que você esteja tentando fazer upload de uma nova página para um site. Digamos que você queira criar uma nova página em http://www.example.com/foo.html, para que você digite seu conteúdo e coloque-o nesse URL. O servidor cria essa página no URL que você fornece. Agora, vamos supor que, por algum motivo, sua conexão de rede fique inoperante. Você não tem certeza, a solicitação foi concluída ou não? Talvez a rede esteja lenta. Talvez houvesse um problema no servidor proxy. Portanto, não há problema em tentar de novo ou de novo - quantas vezes você quiser. Porque COLOCAR o mesmo documento no mesmo URL dez vezes não será diferente de colocá-lo uma vez. O mesmo vale para DELETE. Você pode excluir algo dez vezes e é o mesmo que excluí-lo uma vez.

Por outro lado, o POST, pode causar algo diferente a cada vez. Imagine que você está saindo de uma loja online pressionando o botão comprar. Se você enviar a solicitação POST novamente, poderá acabar comprando tudo no seu carrinho pela segunda vez. Se você o enviar novamente, você o comprará pela terceira vez. É por isso que os navegadores precisam ter muito cuidado ao repetir as operações do POST sem o consentimento explícito do usuário, porque o POST pode causar duas coisas se você fizer isso duas vezes, três coisas se você fizer isso três vezes. Com PUT e DELETE, há uma grande diferença entre zero pedidos e um, mas não há diferença entre um pedido e dez.

Por favor, visite o URL para mais detalhes. http://www.artima.com/lejava/articles/why_put_and_delete.html

Atualizar:

Métodos idempotentes idempotentes Um método HTTP idempotente é um método HTTP que pode ser chamado várias vezes sem resultados diferentes. Não importa se o método é chamado apenas uma ou dez vezes. O resultado deve ser o mesmo. Novamente, isso se aplica apenas ao resultado, não ao próprio recurso. Isso ainda pode ser manipulado (como um registro de data e hora da atualização, desde que essas informações não sejam compartilhadas na representação de recurso (atual)).

Considere os seguintes exemplos:

a = 4;

a ++;

O primeiro exemplo é idempotente: não importa quantas vezes executemos essa instrução, a sempre será 4. O segundo exemplo não é idempotente. A execução disso 10 vezes resultará em um resultado diferente da execução 5 vezes. Como os dois exemplos estão alterando o valor de a, ambos são métodos não seguros.


1
Sobre o exemplo de uma nova página, o POST não deve ser usado dessa maneira, enquanto PUT para uma atualização? Criar uma nova página é um processo que gera um novo resultado toda vez, enquanto a mesma edição pode ser repetida várias vezes, produzindo o mesmo resultado toda vez. Bela frase e explicação, no entanto.
Spyryto

0

Basicamente, o REST é ( wiki ):

  1. Arquitetura cliente-servidor
  2. Apatridia
  3. Cacheability
  4. Sistema em camadas
  5. Código sob demanda (opcional)
  6. Interface uniforme

REST não é protocolo, é princípios. Uris e métodos diferentes - alguém chamado de melhores práticas.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.