Pergunta

Eu estou querendo saber como você pode implementar o seguinte caso de uso em REST. Será que é mesmo possível fazer sem comprometer o modelo conceitual?

ler ou atualizar vários recursos no âmbito de uma única transação. Por exemplo, transferir US $ 100 da conta bancária de Bob em conta de John.

Tanto quanto eu posso dizer, a única maneira de implementar isso é batota. Você pode postar para o recurso associado tanto com John ou Bob e realizar toda a operação usando uma única transação. Tanto quanto eu estou preocupado Isso quebra a arquitetura resto, porque você está tunelamento essencialmente uma chamada RPC através de POST em vez de realmente operando em recursos individuais.

Foi útil?

Solução

Considere um cenário carrinho de compras RESTful. O carrinho de compras está conceitualmente seu wrapper transação. Da mesma forma que você pode adicionar vários itens a um carrinho de compras e, em seguida, enviar essa cesta para processar o pedido, é possível adicionar entrada da conta de Bob para o wrapper transação e, em seguida, entrada da conta de Bill para o wrapper. Quando todas as peças estão no lugar, então você pode POST / PUT o wrapper transação com todas as peças que o compõem.

Outras dicas

Existem alguns casos importantes que não são respondidas por esta questão, que eu acho que é muito ruim, porque ele tem uma alta classificação no Google para os termos de pesquisa: -)

Especificamente, uma boa propertly seria:. Se você postar duas vezes (porque alguns de cache soluçou no intermediário) você não deve transferir o montante duas vezes

Para chegar a isso, você cria uma transação como um objeto. Isso poderia conter todos os dados que você já conhece, e colocar a operação em um estado pendente.

POST /transfer/txn
{"source":"john's account", "destination":"bob's account", "amount":10}

{"id":"/transfer/txn/12345", "state":"pending", "source":...}

Depois de ter esta operação, você pode cometê-lo, algo como:

PUT /transfer/txn/12345
{"id":"/transfer/txn/12345", "state":"committed", ...}

{"id":"/transfer/txn/12345", "state":"committed", ...}

Note que vários puts não importa neste momento; mesmo um GET na txn retornaria o estado atual. Especificamente, o segundo PUT iria detectar que o primeiro já estava no estado apropriado, e apenas devolvê-lo - ou, se você tentar colocá-lo para o estado "rolledback" depois que ele já está em estado "comprometido", você gostaria de obter uma erro, ea parte traseira real transação confirmada.

Enquanto você conversar com um único banco de dados ou um banco de dados com um monitor de operação integrada, este mecanismo irá realmente funcionar muito bem. Você pode também introduzir tempos limite para transações, que você pode até mesmo expressar usando Expira cabeçalhos se você quisesse.

termos em repouso, os recursos são substantivos que podem ser agiram com CRUD (create / ler / update / delete) verbos. Como não há "transferência de dinheiro" verbo, precisamos definir um recurso "transação" que podem ser postas em prática com CRUD. Aqui está um exemplo em HTTP + POX. O primeiro passo é CRIAR (HTTP POST método) um novo esvaziar transação:

POST /transaction

Isso retorna um ID de transação, por exemplo, "1234" e URL de acordo com "/ transacção / 1234". Note-se que a disparar este post várias vezes não irá criar a mesma operação com várias IDs e também evita a introdução de um estado "pendente". Além disso, POST não pode estar sempre idempotent (uma exigência REST), por isso é geralmente uma boa prática para minimizar dados em mensagens.

Você poderia deixar a geração de um ID da transação até o cliente. Neste caso, você POST / transacção / 1234 para criar transação "1234" eo servidor iria retornar um erro se ele já existia. Na resposta de erro, o servidor poderia retornar um ID atualmente não utilizada com um URL apropriado. Não é uma boa idéia para consultar o servidor para um novo ID com um método GET, desde GET deve estado servidor nunca alter, e criar / reservando um novo ID iria alterar o estado do servidor.

Em seguida, nós Atualizar (PUT método HTTP) a operação com todos os dados, comprometendo-se implicitamente:

PUT /transaction/1234
<transaction>
  <from>/account/john</from>
  <to>/account/bob</to>
  <amount>100</amount>
</transaction>

Se uma transação com ID "1234" foi colocado antes, o servidor dá uma resposta de erro, caso contrário, uma resposta OK e uma URL para visualizar a transação concluída.

NB:. In / conta / john, "john" realmente deve ser o número da conta única do John

Ótima pergunta, REST é principalmente explicado com banco de dados como exemplos, onde algo é armazenado, atualizados, recuperados, suprimido. Existem alguns exemplos como este, onde o servidor é suposto para processar os dados de alguma forma. Eu não acho que Roy Fielding incluído qualquer em sua tese, que foi baseado em http depois de tudo.

Mas ele faz palestra sobre "representacional transferência de estado", como uma máquina de estado, com links que se deslocam para o próximo estado. Desta forma, os documentos (as representações) acompanhar o estado do cliente, em vez do servidor ter que fazê-lo. Desta forma, não há estado cliente, único estado em termos de quais links você está.

Eu estive pensando sobre isso, e parece-me razoável que para obter o servidor para processar algo para você, quando você faz o upload, o servidor iria criar automaticamente os recursos relacionados, e dar-lhe os links para eles (na verdade , não seria necessário criá-los automaticamente: poderia apenas dizer-lhe os links, e só criá-los quando e se você segui-las - criação preguiçoso). E também dar-lhe links para criar Novo recursos relacionados - um recurso relacionado tem o mesmo URI, mas é mais longo (acrescenta um sufixo). Por exemplo:

  1. Você faz o upload ( POST ) a representação do conceito de transação com todas as informações. Isso parece apenas como uma chamada RPC, mas é realmente criar a "proposta recurso de transação". por exemplo URI: /transaction Falhas fará com que vários desses recursos a serem criados, cada um com um URI diferente.
  2. A resposta do servidor afirma URI do recurso criado, a sua representação -. Isso inclui o link ( URI ) para criar o recurso relacionado de um novo "recurso transação confirmada" outros recursos relacionados são o elo para excluir a transação proposta. Estes são estados no estado-máquina, que o cliente pode seguir. Logicamente, estes são parte do recurso que tenha sido criado no servidor, além da informação que o cliente fornecido. v.g. URIs: /transaction/1234/proposed, /transaction/1234/committed
  3. Você POST para o link para criar o "recurso transação confirmada" , o que cria esse recurso, alterando o estado do servidor (os saldos de duas contas) **. Por sua natureza, este recurso só pode ser criada uma vez e não pode ser atualizado. Portanto, falhas que cometem muitas transações não pode ocorrer.
  4. Você pode obter esses dois recursos, para ver o que seu estado é. Supondo que um POST pode mudar outros recursos, a proposta agora ser marcada como "comprometido" (ou talvez, não disponível em todas).

Esta é semelhante à forma como webpages operar, com a página da Web final, dizendo "você tem certeza que quer fazer isso?" Essa página final é em si uma representação do estado da transação, que inclui um link para ir para o próximo estado. transações não apenas financeiros; também (por exemplo) de visualização, em seguida, cometer na wikipedia. Eu acho que a distinção em REST é que cada etapa da seqüência de estados tem um nome explícito (o seu URI).

Na vida real transações / vendas, muitas vezes há diferentes documentos físicos para diferentes fases de uma transacção (proposta, ordem de compra, recebimento etc). Ainda mais para comprar uma casa, com liquidação etc.

OTOH Isto parece jogar com a semântica para mim; Estou desconfortável com a nominalization de converter verbos em substantivos para torná-lo RESTful "porque ele usa substantivos (URI) em vez de verbos (chamadas RPC)". ou seja, o substantivo "de recursos comprometidos transação" em vez do verbo "cometer esta transação". Eu acho que uma das vantagens de nominalization é que você pode se referir ao recurso pelo nome, em vez de precisar especificá-lo de alguma outra forma (como manter o estado da sessão, para que você saiba o que "isto" transação é ...)

Mas a questão importante é: Quais são os benefícios desta abordagem? ou seja, In o caminho é este estilo REST melhor do que RPC-estilo? É uma técnica que é ótimo para páginas web também útil para o processamento de informações, além de armazenar / recuperar / update / delete? Eu acho que o principal benefício do REST é a escalabilidade; um aspecto que não é a necessidade de manter o estado do cliente explicitamente (mas tornando-se implícito no URI do recurso, e os próximos estados como links em sua representação). Nesse sentido, ajuda. Talvez isso ajude em camadas / pipelining também? OTOH apenas o usuário vai olhar para a sua transação específica, por isso não há vantagem em cache-lo para que outros possam lê-lo, a grande vitória para http.

Se você ficar para trás para resumir a discussão aqui, é bastante claro que o descanso não é apropriado para muitas APIs, particularmente quando a interação cliente-servidor é inerentemente stateful, como é com transações não-triviais. Por que saltar através de todos os aros sugerido, por cliente e servidor ambos, a fim de pedantically seguir algum princípio de que não se encaixa no problema? Um princípio melhor é dar ao cliente a maneira mais fácil, mais natural e produtiva para compor com o aplicativo.

Em resumo, se você está realmente fazendo um monte de transações (tipos, não instâncias) em seu aplicativo, você realmente não deve ser a criação de uma API RESTful.

Você tem que rolar o seu próprio tipo "ID de transação" de tx de gestão. Portanto, seria 4 chamadas:

http://service/transaction (some sort of tx request)
http://service/bankaccount/bob (give tx id)
http://service/bankaccount/john (give tx id)
http://service/transaction (request to commit)

Você tem que lidar com o armazenamento das ações em um DB (se balanceamento de carga) ou na memória ou tal, então lidar com commit, rollback, timeout.

Não é realmente um dia de descanso no parque.

Eu já se afastaram este tema há 10 anos. Voltando, eu não posso acreditar que a religião disfarçada de ciência que você percorrer em quando você google resto + confiável. A confusão é mítico.

Gostaria de dividir esta questão ampla em três:

  • serviços a jusante. Qualquer serviço web a desenvolver terá serviços a jusante que você usa, e cuja sintaxe transação que você não tem escolha a não ser seguir. Você deve tentar esconder tudo isso de usuários de seu serviço, e certifique-se todas as partes de sua operação ter sucesso ou falhar como um grupo, em seguida, retornar esse resultado para os usuários.
  • Seus serviços. Os clientes querem resultados inequívocos para chamadas de serviço web, eo padrão RESTO normal de fazer POST, PUT ou pedidos de DELETE diretamente dos recursos substanciais me parece uma maneira pobre, e facilmente melhorado, de fornecer essa certeza. Se você se preocupa com confiabilidade, você precisa identificar solicitações de ações. Este ID pode ser um guid criado no cliente, ou um valor de semente a partir de um banco de dados relacional no servidor, não importa. Para servidor gerado de identificação, um pedido de resposta é dedicado a trocar o id. Se este pedido falhar ou meia-sucedido, não há problema, o cliente apenas repete o pedido. ids não utilizados não fazer mal.
    Isto é importante porque permite que todos os pedidos subsequentes ser totalmente idempotent, no sentido de que se forem repetidas n vezes eles retornam o mesmo resultado e causa mais nada para acontecer. O servidor armazena todas as respostas contra a ID de ação, e se ele vê a mesma solicitação, ele repete a mesma resposta. Um tratamento mais completo do padrão é em este google doc . O doc sugere uma implementação que, eu acredito que (!), Segue em linhas gerais princípios REST. Especialistas certamente irá me dizer como ele viola outros. Este padrão pode ser utilmente empregado para qualquer chamada inseguro para o seu web-service, ou não existem transações jusante envolvidos.
  • Integração do seu serviço em "transações" controladas por serviços a montante. No contexto da web-services, transações ACID completos são considerados geralmente não vale o esforço, mas você pode ajudar muito os consumidores de seu serviço, fornecendo cancelar e / ou ligações confirmar por sua resposta de confirmação, e, assim, alcançar transações de compensação .

Sua exigência é um direito fundamental. Não deixe que as pessoas dizer-lhe a sua solução não é kosher. Julgar suas arquiteturas à luz de quão bem, e como simplesmente, resolver o seu problema.

Antes de tudo dinheiro transferindo há nada que você não pode fazer em uma única chamada de recursos. A ação que você quer fazer é enviar dinheiro. Então você adicionar um recurso de transferência de dinheiro para a conta do remetente.

POST: accounts/alice, new Transfer {target:"BOB", abmount:100, currency:"CHF"}.

Feito. Você não precisa saber que esta é uma transação que deve ser atômica etc. Você simplesmente transferir dinheiro aka. enviar dinheiro de A para B.


Mas, para os casos raros aqui uma solução geral:

Se você quer fazer algo muito complexo que envolve muitos recursos em um contexto definido com um monte de restrições que realmente cruzam o que vs. porque barreira (conhecimento implementação de negócios vs.) você precisa estado de transferência. Desde resto deve ser apátrida você como uma necessidade do cliente para transferir o estado ao redor.

Se você transferir o estado que você precisa para esconder a informação privilegiada do cliente. O cliente não deve saber informações internas só precisava pela implementação, mas não carrega informações relevantes em termos de negócios. Se essas informações não têm valor de negócios do estado devem ser criptografados e uma metáfora como forma, passar ou algo necessidade de ser utilizado.

Desta forma, pode passar em torno de estado interno e usando criptografia e assinatura do sistema pode ser ainda ser seguro e som. Encontrar a abstração certa para o cliente por que ele passa em torno de informações de estado é algo que cabe ao design e arquitetura.


A solução real:

Lembre-se REST está falando HTTP e HTTP vem com o conceito de usar cookies. Esses cookies são muitas vezes esquecidos quando as pessoas falam sobre o REST API e fluxos de trabalho e interações abrangendo vários recursos ou pedidos.

Lembre-se que está escrito na Wikipedia sobre cookies HTTP:

Os cookies foram projetados para ser um mecanismo confiável para sites a lembrar informações stateful (como itens em um carrinho de compras) ou para gravar a atividade de navegação do usuário (incluindo clicando em botões específicos, o login, ou gravação que páginas foram visitadas pelo usuário já em meses ou anos atrás).

Então, basicamente, se você precisa passar no estadual, usar um cookie. Ele é projetado para exatamente a mesma razão, é HTTP e, portanto, ele é compatível para descansar por design:).


A melhor solução:

Se você falar sobre um cliente executar um fluxo de trabalho envolvendo várias solicitações que normalmente falam sobre protocolo. Toda forma de protocolo vem com um conjunto de condições prévias para cada etapa potencial como executar a etapa A, antes que você pode fazer B.

Esta é natural, mas expondo protocolo para clientes torna tudo mais complexo. A fim de evitar isso só acho que o que fazemos quando temos que fazer interações complexas e coisas no mundo real .... Nós usamos um agente.

Usando o agente metáfora você pode fornecer um recurso que pode executar todas as etapas necessárias para você e armazenar os reais atribuição / instruções que está agindo sobre em sua lista (para que possamos usar POST sobre o agente ou um 'agência').

Um exemplo complexo:

Comprar uma casa:

Você precisa provar sua credibilidade (como fornecer suas entradas registro policial), você precisa garantir que os detalhes financeiros, você precisa comprar a casa real usando um advogado e um confiável terceiro armazenar os fundos, verificar que a casa agora pertence para você e adicionar o material de compra para seus registros fiscais, etc. (apenas como exemplo, alguns passos podem ser errado ou o que for).

Esses passos podem levar vários dias para ser concluída, alguns podem ser feitas em paralelo etc.

A fim de fazer isso, você apenas dar o agente da casa buy tarefa como:

POST: agency.com/ { task: "buy house", target:"link:toHouse", credibilities:"IamMe"}.

Feito. A agência envia de volta uma referência para você que você pode usar para ver e acompanhar o status de este trabalho e o resto é feito automaticamente pelos agentes da agência.

Pense em um bug tracker por exemplo. Basicamente você relatar o erro e pode usar a ID de erro para verificar o que está acontecendo. Você podeaté mesmo usar um serviço para ouvir as mudanças deste recurso. Missão Concluído.

Eu acho que, neste caso, é totalmente aceitável para quebrar a teoria pura do REST nesta situação. Em qualquer caso, eu não acho que há alguma coisa realmente em REST que diz que você não pode tocar em objetos dependentes em casos de negócios que exigem-lo.

Eu realmente acho que não vale a pena os aros extras que você iria saltar através de criar um gerenciador de transações costume, quando você poderia apenas aproveitar o banco de dados para fazê-lo.

Você não deve usar as transações do lado do servidor em REST.

Um dos constrangimentos de lazer:

Stateless

A comunicação cliente-servidor é igualmente limitada pela nenhum contexto cliente que está sendo armazenado no servidor entre os pedidos. Cada solicitação de qualquer cliente contém todas as informações necessárias para atender à solicitação, e qualquer estado da sessão é realizada no cliente.

A única maneira RESTful é criar um log redo transação e colocá-lo no estado cliente. Com os pedidos do cliente envia o log de redo e o servidor refaz a transação e

  1. rola a volta transação, mas fornece um registro nova transação redo (um passo adiante)
  2. ou, finalmente, completar a transação.

Mas talvez seja mais simples de usar uma tecnologia baseada sessão do servidor que suporta as transações do lado do servidor.

Eu acredito que seria o caso de usar um identificador exclusivo gerado no cliente para garantir que o soluço conexão não implica em uma duplicidade salvo pela API.

Eu acho que usando um campo GUID do cliente gerado juntamente com o objeto de transferência e garantir que o mesmo GUID não foi reinserido novamente seria uma solução simples para o problema de transferência bancária.

não sei cenários sobre mais complexas, tais como múltiplos de reserva em passagem aérea ou arquiteturas de micro.

Eu encontrei um artigo sobre o assunto, relacionando as experiências de lidar com a atomicidade de transações em serviços RESTful .

No caso simples (sem recursos distribuídos), você poderia considerar a transação como um recurso, onde o ato de criar ela alcança o objetivo final.

Assim, a transferência entre <url-base>/account/a e <url-base>/account/b, você poderia postar o seguinte para <url-base>/transfer.

<transfer>
    <from><url-base>/account/a</from>
    <to><url-base>/account/b</to>
    <amount>50</amount>
</transfer>

Isso criaria um novo recurso de transferência e retornar o novo url da transferência -. Por exemplo <url-base>/transfer/256

No momento da pós bem sucedida, então, a transação 'real' é realizada no servidor, ea quantidade removida de uma conta e adicionados a outra.

Isso, no entanto, não cobre uma transação distribuída (se, por exemplo 'a' é realizada em um banco atrás de um serviço, e 'b' é realizada em outro banco atrás de outro serviço) - além de dizer "try a frase todas as operações de maneiras que não exigem transações distribuídas".

Eu acho que você poderia incluir a TAN na URL / recursos:

  1. PUT / transação para obter o ID (por exemplo, "1")
  2. [PUT, GET, POST, seja qual for] / 1 / conta / bob
  3. [PUT, GET, POST, seja qual for] / 1 / conta / fatura
  4. transação / DELETE com ID 1

Apenas uma idéia.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top