Estou planejando criar a API RESTfull, mas há algumas questões de arquitetura que estão criando alguns problemas na minha cabeça. Adicionar lógica de negócios de back-end aos clientes é uma opção que eu gostaria de evitar, pois é difícil manter a atualização de várias plataformas de clientes em tempo real, quando a lógica de negócios pode mudar rapidamente.
Digamos que tenhamos artigo como recurso (API / artigo), como devemos implementar ações como publicar, cancelar publicação, ativar ou desativar e assim por diante, mas para tentar mantê-lo o mais simples possível?
1) Devemos usar api / article / {id} / {action}, já que muita lógica de back-end pode acontecer lá, como enviar para locais remotos ou alterar várias propriedades. Provavelmente, a coisa mais difícil aqui é que precisamos enviar todos os dados do artigo de volta à API para atualização e o trabalho multiusuário não pôde ser implementado. Por exemplo, o editor poderia enviar dados mais antigos de 5 segundos e substituir a correção que algum outro jornalista fez há apenas 2 segundos atrás, e não há como explicar isso aos clientes, uma vez que aqueles que publicam um artigo não estão realmente conectados à atualização do conteúdo.
2) Criar novo recurso também pode ser uma opção, api / article- {action} / id, mas o recurso retornado não seria article- {action}, mas artigo que não tenho certeza se isso é apropriado. Também na classe de artigo de código do lado do servidor está lidando com o trabalho real nos dois recursos e não tenho certeza se isso vai contra o pensamento REST
Todas as sugestões são bem-vindas ..
api/article?action=publish
? Os parâmetros de consulta destinam-se a casos em que o estado do recurso depende do 'algoritmo' (ou ação) mencionado. Por exemplo,api/articles?sort=asc
é válidoRespostas:
As práticas descritas aqui são úteis:
fonte
/article/123/deactivations
para o POST para criar uma nova solicitação de desativação para o artigo 123, o servidor poderá não apenas desativar o recurso solicitado, mas também armazenar minha solicitação de desativação para recuperar seu status posteriormente.PUT /gists/:id/star
nãoPOST /gists/:id/star
?Operações que resultam em grandes mudanças de estado e comportamento no lado do servidor, como a ação "publicar" que você descreve, são difíceis de modelar explicitamente no REST. Uma solução que vejo frequentemente é conduzir comportamentos tão complexos implicitamente através dos dados.
Considere fazer o pedido de mercadorias por meio de uma API REST exposta por um comerciante on-line. Encomendar é uma operação complexa. Vários produtos serão embalados e enviados, sua conta será cobrada e você receberá um recibo. Você pode cancelar seu pedido por um período limitado e, é claro, há uma garantia de devolução total do dinheiro, que permite o envio de produtos para reembolso.
Em vez de uma operação de compra complexa, essa API pode permitir que você crie um novo recurso, um pedido de compra. No início, você pode fazer as modificações desejadas: adicionar ou remover produtos, alterar o endereço de entrega, escolher outra opção de pagamento ou cancelar seu pedido completamente. Você pode fazer tudo isso porque ainda não comprou nada, está apenas manipulando alguns dados no servidor.
Depois que seu pedido de compra é concluído e seu período de carência passa, o servidor bloqueia seu pedido para evitar outras alterações. Somente nesse momento a sequência complexa de operações é iniciada, mas você não pode controlá-la diretamente, apenas indiretamente através dos dados inseridos anteriormente no pedido.
Com base na sua descrição, "publicar" pode ser implementado dessa maneira. Em vez de expor uma operação, você coloca uma cópia do rascunho que analisou e deseja publicar como um novo recurso em / publicar. Isso garante que as atualizações subseqüentes ao rascunho não serão publicadas, mesmo que a operação de publicação seja concluída horas depois.
fonte
Esse tipo de coisa é um desafio, não importa o que você faça. É um problema muito semelhante ao controle de fonte distribuído (mercurial, git etc.), e a solução, escrita em HTTP / ReST, parece um pouco semelhante.
Supondo que você tenha dois usuários, Alice e Bob, ambos trabalhando
/articles/lunch
. (para maior clareza, a resposta está em negrito)Primeiro, Alice cria o artigo.
O servidor não criou um recurso, porque não havia "versão" anexada à solicitação (assumindo um identificador de
/articles/{id}/{version}
. Para executar a criação, Alice foi redirecionada para o URL do artigo / versão que criará. O usuário de Alice O agente reaplicará a solicitação no novo endereço.E agora o artigo foi criado. Em seguida, Bob analisa o artigo:
Bob olha para lá:
Ele decide adicionar sua própria mudança.
Assim como Alice, Bob é redirecionado para onde ele criará uma nova versão.
Por fim, Alice decide que gostaria de adicionar ao seu próprio artigo:
Em vez de ser redirecionado normalmente, um código de status diferente é retornado ao cliente
409
, que informa a Alice que a versão da qual ela estava tentando ramificar já foi ramificada. Os novos recursos foram criados de qualquer maneira (conforme mostrado noLocation
cabeçalho) e as diferenças entre os dois foram incluídas no corpo da resposta. Alice agora sabe que o pedido que ela acabou de fazer precisa ser mesclado de alguma maneira.Todo esse redirecionamento está relacionado à semântica de
PUT
, o que exige que novos recursos sejam criados exatamente onde a linha de solicitação solicita. isso também poderia salvar um ciclo de solicitação usandoPOST
, mas o número da versão teria que ser codificado na solicitação por alguma outra mágica, o que me pareceu menos óbvio para fins de ilustração, mas provavelmente ainda seria preferido em uma API real para minimizar os ciclos de solicitação / resposta.fonte
Aqui está outro exemplo que lida não com o conteúdo dos documentos, mas mais com o estado transitório. (Acho que a versão - já que, em geral, cada versão pode ser um novo recurso - é um tipo de problema fácil.)
Digamos que eu queira expor um serviço em execução em uma máquina por meio de um REST para que ele possa ser parado, iniciado, reiniciado e assim por diante.
Qual é a abordagem mais RESTful aqui? POST / service? Command = restart, por exemplo? Ou POST / service / state com um corpo de, digamos, 'executando'?
Seria bom codificar as práticas recomendadas aqui e se o REST é a abordagem correta para esse tipo de situação.
Segundo, suponha que eu queira conduzir alguma ação de um serviço que não afeta seu próprio estado, mas desencadeia um efeito colateral. Por exemplo, um serviço de mala direta que envia um relatório, construído no momento da chamada, para vários endereços de email.
GET / report pode ser uma maneira de obter uma cópia do relatório; mas e se quisermos enviar para o lado do servidor outras ações, como enviar e-mail, como eu disse acima? Ou escrevendo em um banco de dados.
Esses casos dançam em torno da divisão de recursos e ações, e vejo maneiras de lidar com eles de uma maneira orientada a REST, mas, francamente, parece um hack para isso. Talvez a questão principal seja se uma API REST deve suportar efeitos colaterais em geral.
fonte
O REST é orientado a dados e, como tais recursos, funciona melhor como "coisas" e não ações. A semântica implícita dos métodos http; GET, PUT, DELETE, etc servem para reforçar a orientação. POST, é claro, é a exceção.
Um recurso pode ser uma mistura de dados, ou seja. conteúdo do artigo; e metadados, ie. publicado, bloqueado, revisão. Existem muitas outras maneiras possíveis de dividir os dados, mas é necessário analisar primeiro como será o fluxo de dados para determinar a melhor opção (se houver). Por exemplo, pode ser que as revisões sejam seus próprios recursos no artigo, como sugere o TokenMacGuy.
Em relação à implementação, eu provavelmente faria algo como o que o TockenMacGuy sugere. Eu também adicionaria campos de metadados no artigo, não na revisão, como 'bloqueado' e 'publicado'.
fonte
Não pense nisso como uma manipulação direta do estado do artigo. Em vez disso, você está solicitando uma alteração solicitando a criação do artigo.
Você pode modelar a colocação de uma requisição de mudança como criando um novo recurso de requisição de mudança (POST). Há muitas vantagens. Por exemplo, você pode especificar uma data e hora futuras em que o artigo deve ser publicado como parte da requisição de mudança e deixar o servidor se preocupar com a implementação.
Se a publicação não for um processo instantâneo, não será necessário aguardar o término antes de retornar ao cliente. Você apenas reconhece que a requisição de mudança foi criada e retorna o ID da requisição de mudança. Você pode usar o URL correspondente a essa requisição de mudança para compartilhar o status da requisição de mudança.
Um insight importante para mim foi reconhecer essa metáfora da requisição de mudança é apenas outra maneira de descrever a programação orientada a objetos. Em vez de recursos, chamamos objetos. Em vez de requisições de mudança, as chamamos de mensagens. Uma maneira de enviar uma mensagem de A para B no OO é fazer com que A chame um método em B. Outra maneira de fazer isso, principalmente quando A e B estão em computadores diferentes, é fazer com que A crie um novo objeto, M e envie para B. O REST simplesmente formaliza esse processo.
fonte
Se o entendi corretamente, acho que você tem mais um problema de determinação de 'regra de negócios' do que um problema técnico.
O fato de um artigo poder ser substituído pode ser resolvido com a introdução de níveis de autorização nos quais usuários seniores podem substituir as versões de usuário júnior. Também com a introdução de versões e de uma coluna para capturar o estado do artigo (por exemplo, 'em desenvolvimento', 'final' , etc.), você pode superar isso. Você também pode dar ao usuário a capacidade de selecionar uma versão específica combinando o tempo de envio e o número da versão.
Em todos os casos acima, seu serviço precisa implementar as regras de negócios definidas por você. Portanto, você pode chamar o serviço com os parâmetros: ID do usuário, artigo, versão, ação (onde a versão é opcional, novamente isso depende das regras de negócios).
fonte