Há algum problema com a implementação de métodos HTTP personalizados?

34

Temos um URL no seguinte formato

/ instance / {instanceType} / {instanceId}

Você pode chamá-lo com os métodos HTTP padrão: POST, GET, DELETE, PUT. No entanto, existem mais algumas ações que realizamos, como "Salvar como rascunho" ou "Organizar"

Pensamos que poderíamos usar métodos HTTP personalizados, como: DRAFT, VALIDATE, CURATE

Eu acho que isso é aceitável, já que os padrões dizem

"O conjunto de métodos comuns para HTTP / 1.1 está definido abaixo. Embora esse conjunto possa ser expandido, não se pode presumir que métodos adicionais compartilhem a mesma semântica para clientes e servidores estendidos separadamente".

E ferramentas como o WebDav criam algumas de suas próprias extensões.

Existem problemas nos quais alguém se deparou com métodos personalizados? Estou pensando em servidores proxy e firewalls, mas qualquer outra área de preocupação é bem-vinda. Devo permanecer no lado seguro e ter apenas um parâmetro de URL como action = validate | curate | draft?

Juan Mendes
fonte
6
Como cito novamente a RFC 1925 - "No design de protocolo, a perfeição foi alcançada não quando não há mais nada a acrescentar, mas quando não há mais nada a ser retirado". - se funcionar, não há motivo para adicionar ao http.
4
Nada de errado, desde que você perceba que agora está usando um protocolo personalizado e não HTTP.
user16764
10
@ user16764 "O conjunto de métodos comuns para HTTP / 1.1 está definido abaixo. Embora esse conjunto possa ser expandido, não é possível presumir que métodos adicionais compartilhem a mesma semântica para clientes e servidores estendidos separadamente." w3.org/Protocols/rfc2616/rfc2616-sec9.html Por isso é permitido, e ainda HTTP é
Juan Mendes
imho não há nada para adicionar / remover do HTTP, pois as definições de método afirmam que o uso de métodos personalizados já é aceitável no escopo HTTP / 1.1, mas não se pode esperar que compartilhem a mesma semântica, então acho que os pontos de @MichaelT e Juan Mendes podem ser um pouco apaziguado
Prof83

Respostas:

42

Uma das restrições fundamentais do HTTP e o recurso de design central do REST é uma interface uniforme fornecida por (entre outras coisas) um pequeno conjunto fixo de métodos que se aplica universalmente a todos os recursos. A restrição de interface uniforme tem várias vantagens e desvantagens. Estou citando liberalmente aqui Fielding .

Uma interface uniforme:

  • é mais simples.
  • desacopla implementações dos serviços que eles fornecem.
  • permite uma arquitetura em camadas, incluindo itens como balanceadores de carga HTTP (nginx) e caches (verniz).

Por outro lado, uma interface uniforme:

  • degrada a eficiência, porque as informações são transferidas de forma padronizada e não específica para as necessidades de um aplicativo.

As compensações são "projetadas para o caso comum da Web" e permitiram a construção de um grande ecossistema que fornece soluções para muitos dos problemas comuns nas arquiteturas da Web. A adesão a uma interface uniforme permitirá que seu sistema se beneficie desse ecossistema ao quebrá-lo, tornando-o difícil. Você pode querer usar um balanceador de carga como o nginx, mas agora só pode usar um balanceador de carga que entenda DRAFT e CURATE. Você pode querer usar uma camada de cache HTTP como o Varnish, mas agora só pode usar uma camada de cache HTTP que entenda DRAFT e CURATE. Convém pedir ajuda a alguém para solucionar uma falha do servidor, mas ninguém mais conhece a semântica de uma solicitação CURATE. Pode ser difícil alterar as bibliotecas preferidas de cliente ou servidor para entender e implementar corretamente os novos métodos. E assim por diante.

A maneira correta * de representar isso é como uma transformação de estado no recurso (ou recursos relacionados). Você não esboça uma postagem, transforma seu draftestado trueou cria um draftrecurso que contém as alterações e os links para as versões de rascunho anteriores. Você não CURA uma postagem, transforma seu curatedestado trueou cria um curationrecurso que vincula a postagem ao usuário que a selecionou.

* Corrija que segue mais de perto os princípios de arquitetura REST.

Rein Henrichs
fonte
Obrigado pelos comentários sobre o balanceamento de carga, eu definitivamente vou dar uma olhada nisso. Você conhece um recurso que declara se os métodos personalizados são aceitáveis ​​ou não?
Juan Mendes
2
Não vejo nenhuma vantagem nos métodos personalizados, a menos que façam parte de uma extensão amplamente suportada, como WEBDAV (e mesmo assim, nem tanto), por isso nunca procurei. Eu recomendaria apenas que você trate essas alterações como transformações de estado. A web funciona bem com os métodos que já temos. Não há realmente nenhuma boa razão para adicionar mais, a menos que façam sentido como parte da interface uniforme (como PATCH).
precisa saber é o seguinte
5
Vejo a vantagem de projetar a maneira como você deseja que seu serviço HTTP funcione para si mesmo. no entanto, "não se pode presumir que métodos adicionais compartilhem a mesma semântica" - é justo, mas ainda faz parte do escopo HTTP / 1.1; portanto, firewalls, proxies, balanceadores de carga e similares devem permitir essa possibilidade, se não Então eles não estão implementando o HTTP / 1.1 corretamente?
Prof83
Você provavelmente está certo do ponto de vista tranquilo, no entanto, não consigo entender por que os verbos personalizados devem ser um problema. Todas as ferramentas devem tratá-las exatamente como o POST, ou seja, "o recurso provavelmente muda e é tudo o que sabemos".
Maaartinus 5/0518
7

Eu preferiria projetá-los como sub-recursos, nos quais você executa uma solicitação POST.

Considere que você tem um recurso em /instance/type/1, gostaria que a representação desse recurso transmitisse alguns links para 'ações' que podem ser executadas no recurso, como /instance/type/1/drafte /instance/type/1/curate. No JSON, isso pode ser tão simples quanto:

{
    "some property":"the usual value",
    "state": "we can still inform the client about the current state",
    "draft": "http://server/instance/type/1/draft",
    "curate": "http://server/instance/type/1/curate"
}

Isso permite que o cliente seja muito explícito sobre o que precisa acontecer, durante a solicitação POST para o link fornecido pelo curatemembro. O recurso publicado lá pode incluir argumentos que detalham o evento que talvez infligisse uma transição de estado.

Seguir a abordagem "ingênua" de mover-se entre os possíveis estados de um recurso tem a desvantagem de não capturar quais eventos levaram a essas transições.

As transições de estado geralmente ocorrem em resposta a eventos específicos, e eu prefiro capturá-los do que deixar que o cliente decida que algo está agora em um 'estado' específico. Também torna a validação muito mais difícil. Além disso, você não seria capaz de capturar nenhum 'argumento' a menos que os descrevesse no próprio estado. E então tudo fica nojento quando algum código altera aqueles sem transição de estado real e a validação necessária, e tudo rapidamente se torna uma bagunça.

Dave Van den Eynde
fonte
Boa resposta. Ser capaz de fornecer argumentos para transições de estado e fazer com que o servidor encapsule e gerencie essas são de longe a melhor abordagem.
Thomas W
A empresa em que estou atualmente (VMware) faz isso. Um GET on /vms/some-idretorna links para ações como POST /vms/some-id/restarte usamos para determinar se as ações devem ser ativadas ou desativadas. Eu tenho uma relação de amor / ódio com HATEOAS :)
Juan Mendes
Faria muito mais sentido se a ação que estivesse sendo executada fosse o verbo da solicitação em comparação com algum parâmetro de consulta aleatória, segmento do caminho do recurso ou propriedade do corpo.
Matthew Whited
Você não pode vincular a um verbo.
Dave Van den Eynde
6

Eu acho que o método HTTP personalizado é a melhor maneira de implementar ações de entidade. Adicionar a ação ao corpo da entidade (POST) não parece certo, não faz parte da sua entidade (embora o resultado possa ser salvo nela). Além disso, o uso dos proxies de métodos HTTP personalizados pode determinar suas ações sem a necessidade de analisar o corpo da entidade.

É como CRUD, você sempre desejaria implementá-las, mas também possui seu próprio conjunto de ações específico (por entidade). Realmente não vejo qual seria o problema de estendê-los.

Também @Rein Henrichs "Você não desenha uma postagem, transforma seu estado de rascunho em verdadeiro ou cria um recurso de rascunho" parece falso para mim. Uma draftspropriedade seria usada para salvar persistentemente o estado, não para fazer a transformação. Ações nem necessariamente resultam em um 'estado' ou são salvas em uma propriedade. Criar uma entidade separada para cada estado / transformação parece ainda mais confuso. Tente manter a mesma referência (URI) para a entidade.

Robert de W
fonte
1
Este é um ponto justo, embora discorde amplamente, posso ver o raciocínio por trás dele e não concordo com o voto negativo (especialmente sem nenhum comentário do eleitor). Vamos considerar o tratamento de exceções do PHP como um exemplo: "Best Practice" parece inclinar-se a usar tipos de exceção específicos para sugerir o tipo de exceção, mesmo ignorando a mensagem real como RuntimeException vs BadMethodCallException. Então, por que é tão amplamente contestado o uso do DRAFT se o uso de métodos personalizados já é considerado parte do escopo HTTP / 1.1? E os balanceadores de carga e proxies também devem estar aceitando essa possibilidade também.
Prof83