RESTFul: ações de mudança de estado

52

Estou planejando criar a API RESTfull, mas há algumas questões arquitetônicas que estão criando alguns problemas na minha cabeça. Adicionar lógica de negócios de back-end a clientes é uma opção que eu gostaria de evitar, pois é difícil manter a atualização em várias plataformas de clientes em tempo real, quando a lógica de negócios pode mudar rapidamente.

Digamos que temos um artigo como recurso (api / article), como devemos implementar ações como publicar, cancelar publicação, ativar ou desativar e assim por diante, mas tentar mantê-lo o mais simples possível?

1) Devemos usar api / article / {id} / {action}, já que muita lógica de backend pode acontecer, como o envio para locais remotos ou a alteração de várias propriedades. Provavelmente, o 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 5 segundos a dados mais antigos e substituir a correção que algum outro jornalista acabou de fazer há 2 segundos e não há como eu explicar isso aos clientes, já que aqueles que publicam um artigo não estão de forma alguma conectados à atualização do conteúdo.

2) Criar um novo recurso também pode ser uma opção, api / article- {action} / id, mas o recurso retornado não seria um artigo- {action}, mas um artigo que eu 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 em ambos os recursos e não tenho certeza se isso vai contra o pensamento RESTfull

Todas as sugestões são bem-vindas.

    
por Miro Svrtan 25.03.2012 / 12:12
fonte

7 respostas

39

Acho as práticas descritas aqui úteis:

What about actions that don't fit into the world of CRUD operations?

This is where things can get fuzzy. There are a number of approaches:

  1. Restructure the action to appear like a field of a resource. This works if the action doesn't take parameters. For example an activate action could be mapped to a boolean activated field and updated via a PATCH to the resource.
  2. Treat it like a sub-resource with RESTful principles. For example, GitHub's API lets you star a gist with PUT /gists/:id/star and unstar with DELETE /gists/:id/star.
  3. Sometimes you really have no way to map the action to a sensible RESTful structure. For example, a multi-resource search doesn't really make sense to be applied to a specific resource's endpoint. In this case, /search would make the most sense even though it isn't a resource. This is OK - just do what's right from the perspective of the API consumer and make sure it's documented clearly to avoid confusion.
    
por 17.01.2015 / 20:24
fonte
9

As 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 implicitamente esse comportamento complexo por meio de dados.

Considere o pedido de mercadorias por meio de uma API REST exposta por um comerciante on-line. Ordenar é uma operação complexa. Vários produtos serão embalados e enviados, sua conta será cobrada e você receberá um recibo. Você pode cancelar o seu pedido por um tempo limitado e, claro, há uma garantia total de devolução do dinheiro que lhe permite devolver produtos para um 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 começo, você pode fazer qualquer modificação que desejar: adicionar ou remover produtos, alterar o endereço de entrega, escolher outra opção de pagamento ou cancelar seu pedido. Você pode fazer tudo isso porque você não comprou nada ainda, você está apenas manipulando alguns dados no servidor.

Quando seu pedido de compra for concluído e seu período de carência passar, o servidor bloqueará seu pedido para evitar mais alterações. Somente neste momento a sequência complexa de operações começa, mas você não pode controlá-lo diretamente, apenas indiretamente através dos dados que você colocou 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 você revisou e deseja publicar como um novo recurso em / publish. Isso garante que as atualizações subsequentes do rascunho não sejam publicadas, mesmo que a operação de publicação seja concluída horas depois.

    
por 25.03.2012 / 16:20
fonte
6

we need to send all article data back to API for updating and multiuser work could not be implemented. For instance editor could send 5 seconds older data and overwrite fix that some other journalist just did 2 seconds ago and there is no way that I could explain to clients this since those publishing an article is really not in any way connected to updating the content.

Esse tipo de coisa é um desafio, não importa o que você faça, é um problema muito similar ao controle de fonte distribuída (mercurial, git, etc.), e a solução, escrita em HTTP / ReST, parece um pouco similar. / p>

Suponha que você tenha dois usuários, Alice e Bob, ambos trabalhando em /articles/lunch . (para maior clareza, a resposta está em negrito)

Primeiro, Alice cria o artigo.

PUT /articles/lunch HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

301 Moved Permanently
Location: /articles/lunch/1 

O servidor não criou um recurso, pois não havia "versão" anexada ao pedido, (assumindo um identificador de /articles/{id}/{version} . Para realizar a criação, Alice foi redirecionada para o URL do artigo / versão ela ' O agente do usuário de Alice reaplicará a solicitação no novo endereço.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?

201 Created

E agora o artigo foi criado. Em seguida, Bob olha para o artigo:

GET /articles/lunch HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

301 Moved Permanently
Location: /articles/lunch/1 

Bob olha para lá:

GET /articles/lunch/1 HTTP/1.1
Host: example.com
Authorization: Basic Ym9iOnBhc3N3b3Jk

200 Ok
Content-Type: text/plain

Hey Bob, what do you want for lunch today?

Ele decide adicionar sua própria alteração.

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

301 Moved Permanently
Location: /articles/lunch/2

Assim como Alice, Bob é redirecionado para onde ele criará uma nova versão.

PUT /articles/lunch/2 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic Ym9iOnBhc3N3b3Jk

Hey Bob, what do you want for lunch today?
Does pizza sound good to you, Alice?

201 Created

Finalmente, Alice decide que gostaria de adicionar ao seu próprio artigo:

PUT /articles/lunch/1 HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Basic YWxpY2U6c2VjcmV0

Hey Bob, what do you want for lunch today?
I was thinking about getting Sushi.

409 Conflict
Location: /articles/lunch/3
Content-Type: text/diff

---/articles/lunch/2
+++/articles/lunch/3
@@ 1,2 1,2 @@
 Hey Bob, what do you want for lunch today?
-Does pizza sound good to you, Alice?
+I was thinking about getting Sushi.

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 pelo cabeçalho Location ) 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 forma.

Todo esse redirecionamento está relacionado à semântica de PUT , que requer que novos recursos sejam criados exatamente onde a linha de solicitação é solicitada. Isso também poderia salvar um ciclo de solicitação usando POST , mas o número da versão teria que ser codificado na solicitação por alguma outra mágica, o que parecia menos óbvio para mim para fins de ilustração, mas provavelmente ainda seria preferível em uma API real para minimizar os ciclos de solicitação / resposta.

    
por 25.03.2012 / 17:32
fonte
2

O REST é orientado a dados e, como tal, os recursos funcionam melhor como "coisas" e não como 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 você precisa passar pelo primeiro passo do fluxo de dados para determinar o melhor (se houver). Por exemplo, pode ser que as revisões devam ser seu próprio recurso no artigo, como sugere o TokenMacGuy.

Em relação à implementação, eu provavelmente faria algo parecido com o que o TockenMacGuy sugere. Eu também adicionaria um campo de metadados no artigo, não na revisão, como 'bloqueado' e 'publicado'.

    
por 26.03.2012 / 06:30
fonte
2

Aqui está outro exemplo que não lida com conteúdo de documentos, mas mais com estado transitório. (Eu acho versionamento - dado 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 possa ser interrompido, 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, 'correndo'?

Seria bom codificar as melhores práticas aqui e se o REST é a abordagem correta para esse tipo de situação.

Em segundo lugar, suponhamos que eu queira direcionar alguma ação de um serviço que não afete o seu próprio estado, mas que desencadeie um efeito colateral. Por exemplo, um serviço de mailer que envia um relatório, criado no momento da chamada, para um grupo de endereços de email.

O GET / report pode ser uma maneira de obter uma cópia do relatório por conta própria; mas e se quisermos empurrar para o lado do servidor outras ações, como enviar e-mail como eu digo acima. Ou escrevendo para um banco de dados.

Esses casos dançam em torno da divisão entre recursos e ações, e vejo maneiras de lidar com eles de uma maneira orientada ao REST, mas, francamente, parece um pouco um truque para fazê-lo. Talvez a questão principal seja se uma API REST deve suportar efeitos colaterais em geral.

    
por 22.04.2013 / 07:33
fonte
1

Não pense nisso como manipulando diretamente o estado do artigo. Em vez disso, você está colocando uma ordem de alteração solicitando que o artigo seja criado.

Você pode modelar a colocação de uma requisição de mudança como a criação de um novo recurso de pedido de alteração (POST). Existem muitas vantagens. Por exemplo, você pode especificar uma data e hora futuras quando o artigo deve ser publicado como parte do pedido de alteração e deixar que o servidor se preocupe com a forma como isso é implementado.

Se a publicação não for um processo instantâneo, você não precisará aguardar a conclusão antes de voltar ao cliente. Você acabou de reconhecer que a requisição de mudança foi criada e retorna o ID da requisição de mudança. Você pode então usar o URL correspondente a esse pedido de alteração para compartilhar o status do pedido de alteração.

Um insight fundamental para mim foi reconhecer que essa metáfora da ordem de mudança é apenas outra maneira de descrever a programação orientada a objetos. Em vez de recursos, chamamos então objetos. Em vez de pedidos de alteração, nós os chamamos de mensagens. Uma maneira de enviar uma mensagem de A para B em OO é fazer com que A chame um método em B. Outra maneira de fazê-lo, particularmente quando A e B estão em computadores diferentes, é criar um novo objeto, M e envie-o para B. REST simplesmente formaliza esse processo.

    
por 29.06.2017 / 02:51
fonte
0

Se eu entendi corretamente, acho que o 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 sobrescrito pode ser resolvido com a introdução de níveis de autorização em que os usuários seniores podem substituir as versões do usuário júnior. Também introduzindo versões e 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 determinada versão por uma combinação de tempo de envio e pelo número da versão.

Em todos os casos acima, seu serviço precisa implementar as regras de negócios que você definiu. Assim, você poderia chamar o serviço com os parâmetros: userid, article, version, action (onde a versão é opcional, mais uma vez isso depende de suas regras de negócios).

    
por 25.03.2012 / 13:47
fonte

Tags