Ponto de extremidade REST para mostrar uma visualização antes do POST

17

Estou projetando um novo aplicativo Web que é alimentado por um back-end REST e front-end HTML + JS.

Existe um método POST para alterar uma entidade (vamos chamar de Config), que possui vários efeitos colaterais no estado de muitos elementos do aplicativo. Vamos supor que o POST seja executado desta maneira:

POST /api/config BODY {config: ....}

Por isso, gostaria de mostrar uma prévia antes que essas alterações sejam feitas, para que o usuário final possa perceber o que vai mudar.

A primeira coisa que pensei foi criar um ponto de extremidade GET para a visualização, enviando o corpo do novo estado da entidade. Por aqui:

GET /api/preview/items BODY {config: ....}

Pode mostrar o novo estado para os itens com a nova configuração.

GET /api/preview/sales BODY {config: ....}

Pode mostrar o novo estado das vendas com a nova configuração.

Parece uma boa ideia usar o verbo GET, pois não estou alterando o estado do aplicativo. No entanto, o uso de um corpo de solicitação com solicitações GET parece desencorajado .

Existe alguma boa prática sobre isso? Outra opção pode ser armazenar a configuração como rascunho com um método e exibir os resultados com outros, mas isso exigiria uma etapa adicional e a necessidade de gerenciar os rascunhos no servidor:

POST /api/preview/config BODY {config: ....}

GET /api/preview/items?idPreviewConfig=1
Xtreme Biker restabelece Monica
fonte
O que exatamente poderia ser essa configuração e como isso afeta o itemsor sales? Isso afeta a representação da entidade retornada?
Andy
Vamos supor que os itens e as vendas sejam afetados pelas alterações que você faz na configuração.
Xtreme Biker restabelece Monica
Mas o que as mudanças significam? Isso altera o conjunto de entidades retornadas? Isso muda a estrutura retornada?
187 Andy Andy
Na verdade, ele altera os valores para itemse sales(não a estrutura), dependendo da configuração do POST.
Xtreme Biker restabelece Monica
E qual é o tamanho exato da configuração? Pode crescer até várias centenas de kilobytes ou até mais?
187 Andy Andy

Respostas:

27

Isso é muito específico do domínio para ter um suporte nativo no HTTP.

Em vez disso, você pode executar um dos seguintes procedimentos:

  1. Tenha um POST /api/config/preview. No lado do servidor, o aplicativo saberá que não deve modificar a configuração real, mas combinar a atual com a que você postou e retornar o resultado indicando o que foi alterado.

    Posteriormente, se o usuário estiver satisfeito com o resultado, ele executará uma POST /api/configcontendo a mesma carga útil da solicitação anterior. Isso substituirá efetivamente a configuração.

    O benefício dessa abordagem é que você não está fazendo nenhuma alteração na API atual. Os clientes que não precisam do recurso de visualização ainda poderão atualizar as entradas como antes.

    A desvantagem é que, quando o corpo é grande, isso significa que seria necessário enviá-lo duas vezes ao servidor. Se esse for o seu caso, você pode usar a próxima abordagem.

  2. Tenha um POST /api/config/prepareque lembre o que foi enviado em um registro temporário e retorne duas coisas: o ID do registro temporário (por exemplo 12345) e a visualização das alterações.

    Se o usuário estiver satisfeito com o resultado, ele executará um POST /api/config/commit/12345para armazenar definitivamente as alterações. Caso contrário, o registro temporário pode ser mantido por algum tempo e depois descartado por um trabalho cron.

    O benefício é que, aqui novamente, você pode manter o original POST /api/configintacto, e os clientes que não precisam de uma visualização não serão interrompidos.

    As desvantagens são que (1) lidar com a remoção de registros temporários pode ser complicado (o que faz você pensar que uma hora é suficiente? E se dez minutos depois você ficar sem memória? Como os clientes lidam com um HTTP 404 ao fazer um commit de um registro que expirou?) e que (2) o envio em duas etapas de um registro pode ser mais complicado do que precisa.

  3. Mova a lógica de visualização no lado do cliente.

Arseni Mourzenko
fonte
Que tal enviar um cabeçalho que diz "não persista, apenas me mostre o que se"? Vou editar isso para a resposta se isso é muito bem com você @ArseniMourzenko
marstato
1
@ marstato: pessoalmente, não gosto particularmente de cabeçalhos HTTP para esse uso. Embora possa fazer sentido para outras pessoas, estou bem se você editar minha resposta. Observe que você também pode postar sua própria resposta, o que permitiria que outros a votassem (e obterá pontos de reputação).
Arseni Mourzenko
Eu acho que a opção 1 combina melhor com o meu caso. Portanto, você POSTARá a configuração de visualização e terá as alterações no resultado, em vez de precisar definir pontos de extremidade de visualização para cada uma das entidades definidas. Parece razoável. A única coisa é que você está usando um POST para não alterar o servidor, tecnicamente falando. A opção 3 não é viável no meu caso.
Xtreme Biker restabelece Monica
1
@PedroWerneck Você pode expandir isso? Parece-me que a opção 2 define outra entidade (uma configuração de rascunho) e fornece maneiras sem estado para interagir com elas.
Andrew diz Reinstate Monica
1
@PedroWerneck É stateful da mesma maneira que armazenar uma configuração no servidor é stateful. Portanto, o aplicativo já é estável da sua perspectiva e todas as opções para adicionar esse recurso.
jpmc26
10

O objetivo de usar verbos HTTP específicos para diferentes chamadas de API no REST é aproveitar a mecânica e as expectativas HTTP existentes.

Usar um GET nesse caso parece ir contra os dois.

A. O cliente precisa incluir um corpo com um GET? inesperado

B. O servidor retorna uma resposta diferente para um get, dependendo do corpo? quebra especificações e mecânica de cache

Se você está lutando com perguntas RESTful, minha regra é me perguntar.

"Como isso é melhor do que usar o POST para tudo?"

A menos que haja um benefício imediato e óbvio, siga a estratégia Just Use POST Stupid (JUPS)

Ewan
fonte
Hahaha good catch
Xtreme Biker restabelece Monica
@ Ewan ... independentemente de se tratar de uma abordagem pragmática ou não ... se você estiver usando o POST para tudo, observe que não é realmente RESTful.
Allenph
1
bem, a menos que o POST seja a escolha apropriada para todos os seus métodos. E não é que exista uma regra objetiva que você possa aplicar; estaríamos apenas discutindo nossas interpretações subjetivas do que é pouco mais que uma diretriz.
Ewan
6

Você pode enviar um cabeçalho que indique ao servidor "não persista, apenas me mostre qual seria o resultado se você o fizesse". Por exemplo

POST /api/config HTTP/1.1
Host: api.mysite.com
Content-Type: application/json
Persistence-Options: simulate

{
   "config": {
      "key": "value"
   }
}

Para o qual o servidor pode responder:

HTTP/1.1 200 OK
Persistence-Options: simulated
Content-Type: application/json

-- preview --

Observe que, se você usar transações de O / RM e / ou por solicitação com base no Unit of Work com seu banco de dados, poderá implementar facilmente essa funcionalidade para todos os seus pontos de extremidade sem exigir trabalho em nenhum ponto de extremidade específico: Se um pedido entrar com essa opção , reverta a transação / unidade de trabalho em vez de confirmar.

marstato
fonte
@PeterRader bom ponto, removido oX-
marstato
Não há de quê. Você diria que uma entidade em simulação deve ser representada como "em simulação"?
22817 Peter Rader
Não; esse é o objetivo de uma simulação, não é? O valor do cabeçalho também pode ser, nonemas isso - para meu gosto - contradiz muito a natureza do POSTmétodo.
marstato
2

Eu sugeriria tratar isso da mesma maneira que você trata as pesquisas. Eu configuraria um terminal POST no /api/config/previewqual CREATES uma nova visualização. Então, eu configuraria um ponto de extremidade PUT ou PATCH, api/configdependendo se você pretende editar a configuração atual ou simplesmente substituir a configuração inteira (presumivelmente, no caso anterior, você enviaria a visualização que acabou de criar).

Allenph
fonte
0

Juntamente com outras boas respostas, outra opção poderia ser a postagem da configuração, como mencionado, e também ter um processo de reversão disponível. Eu acho que, como a metodologia Agile, é melhor ter menos medo de mudanças, com procedimentos mais granulares, repetíveis e testados, e isso lhe daria um backup quando necessário, reduzindo o risco para pouco ou nenhum, dependendo do aplicativo .

Por outro lado, se você tiver erros de configuração que afetam todo o sistema, gostaria de lidar com isso de forma mais ativa e, se for o caso, por que não apenas esforçar-se para visualizar as alterações nesse ponto, da perspectiva do servidor ou do cliente. Embora eu possa ver como esse recurso de visualização pode ser mais caro para desenvolver, nos casos de uso há seu próprio conjunto de etapas diferentes para seguir e testar.

Pysis
fonte
0

O RFC6648 reprova novas X-construções, portanto, devo votar contra a ideia de enviar um novo campo de cabeçalho. REST é um estilo de arquitetura, o que falamos é RESTful - mas vamos ignorá-lo neste momento.

Como o REST é representativo (e uma simulação não tem representação na realidade) e com estado (e uma simulação não é um estado até que seja confirmado), precisamos ter um novo escopo, como um escopo de simulação. Mas devemos chamá-lo de emulação em vez de simulação, porque a simulação inclui o processo de simulação, mas com estado significa que temos um estado permanente, a solução ideal de uma simulação: uma emulação. Então, precisamos chamá-lo de emulação no URL. Essa também pode ser uma boa solução:

GET  /api/emulation - 200 OK {first:1, last:123}
POST /api/emulation/124 - 200 OK
GET  /api/emulation/124/config - 200 OK {config:{tax:8}}
PUT  /api/emulation/124/config {config:{tax:16}} - 200 OK {config:{tax:16}}
GET  /api/emulation/124/items - 200 OK [first:1, last: 3000]
GET  /api/emulation/124/items/1 - 200 OK {price:1.79, name:'Cup'}
--- show emulation ---
--- commit emulation ---
PUT /api/config {config:{tax:16}}
DELETE /api/emulation/124 - 200 OK

Existe outra abordagem ... você pode ter notado que muitas solicitações do cliente HTML / JavaScript podem produzir muitas solicitações , o que atinge o limite de cerca de 17 solicitações ao mesmo tempo (veja esta página ). Você pode trocar o uso de REST e, em vez de entregar estados de objeto coxos, pode fornecer estados de página específicos para o usuário. Exemplo:

GET /user/123/config - 200 OK {user:'Tim', date:3298347239847, currentItem:123, 
                  roles:['Admin','Customer'], config:{tax:16}, unsavedChanges:true, ...}

Atenciosamente

Peter Rader
fonte