Representar ações (verbos) no REST URI

16

Tenho uma operação de impressão para executar nos documentos dos meus clientes. Também preciso que outras operações padrão sejam executadas, como adicionar, atualizar e excluir. então, eu tenho o seguinte:

  • Para criar um novo cliente:
    URI = / customer / {id}, digite = POST, Methodname = CreateCustomer ()
  • Para atualização:
    URI: / customer / {id}, digite = PUT, método = UpdateCstomer ()
  • Para Excluir cliente:
    URI = / customer / {id}, digite = DELETE, Nome do método = DeleteCustomer ()
  • Para Ver:
    URI: / cliente / {id}, escreva = GET, method = GetCustomer ()

Agora, se eu precisar imprimir um documento para esse cliente, preciso de uma função de impressão. Meu URI pode ficar assim: / customer / {id}, tipo = POST, método = PrintCustomer (). Mas eu usei esse tipo de URI e POST para CreateCustomer. Eu queria que o URI tivesse a seguinte aparência: / customer / Print / {id}, digite = POST, método = PrintCustomer ().

Mas não posso ter o verbo "Imprimir" no meu URI. Qual é a melhor forma de fazer isso? Pensei em / customer / document / {id} como o URI ... mas vou encontrar o mesmo problema. Eu teria as operações CRUD no "documento". Então, novamente, acabo com o que eu usaria para "imprimir". Por favor informar.

Nitya Maha
fonte
2
A impressão geralmente é uma operação do lado do cliente, por isso estou curioso - como é a sua configuração que exige que você envie um comando para um servidor REST?
Shauna
2
@ Shauna Não necessariamente, o URI pode ser uma solicitação ao servidor para uma versão do recurso que pode ser impressa (ou seja, uma visão diferente).
Evan Solha
1
@EvanPlaice - É justo, embora isso ainda deixe o ato de imprimir para o cliente (que, mesmo depois de buscar uma versão de impressão do lado do servidor, decidia em qual dispositivo imprimir e envia o próprio comando de impressão, mesmo que comando vai para um servidor de impressão). Uma solicitação para obter uma versão de impressão de um recurso seria logicamente ... bem ... GET.
Shauna
@ Shauna O acionamento de um trabalho de impressão a partir de uma solicitação HTTP sozinho seria impossível devido à segurança do navegador. Uma solicitação para uma versão de impressão é apenas uma solicitação GET, mas você ainda precisa especificar como o navegador deve renderizar a versão imprimível. Você pode especificar um URL diferente, mas isso violaria os princípios do REST, porque na verdade você não está solicitando um recurso diferente, apenas uma transformação diferente do mesmo recurso. Daí a razão para especificar a transformação por meio de um parâmetro de consulta e / ou tipo de conteúdo.
quer
Não tenho representante suficiente para postar como resposta, mas acho interessante que tyk.io/rest-never-crud argumenta que isso POST /customers/123/printé uma coisa válida a se fazer.
jlh

Respostas:

9

POSTnão significa "criar", significa "processo". Você pode criar um novo recurso postando uma solicitação adequada em um recurso existente (por exemplo, postar /customerspara criar um novo cliente). Mas você também pode usar POSTpara preencher todas as outras ações que não correspondem a um paradigma CRUD puro.

No caso de impressão, você deve considerar o ato de imprimir como um recurso em si. Você está pedindo ao sistema para criar um "trabalho de impressão" para você. Isso significa que você pode ter um prints/recurso que atua como o contêiner para todas as impressões solicitadas. Quando você deseja imprimir algo em POSTum documento para este recurso, que contém todas as informações sobre a impressão que deseja criar, identificando os recursos que deseja imprimir com links para eles.

Como um documento JSON, poderia ser assim:

{
   contents: ["http://site/customers/12345"],
   paper-size: "A4",
   duplex: "true"
}

Obviamente, você precisa personalizá-lo para ser relevante para o que deseja fazer. O principal é que você esteja identificando outros recursos para imprimir especificando o URL deles.

Em resposta à solicitação, você pode simplesmente devolver um 200 OKou um 204 No-Contente tratá-lo como um processo de ignorar. No entanto, se você quiser aprimorá-lo, poderá retornar 201 Createde especificar o URL do trabalho de impressão recém-criado, por exemplo /prints/12345.

Um usuário pode executar um GETno recurso para ver o status do trabalho de impressão (pendente, em andamento etc.) ou pode solicitar que o trabalho seja cancelado com a emissão de a DELETE.

Depois de reformular o problema em termos de recurso, o design do RESTful deve vir naturalmente e dar a você a oportunidade de expandir e aprimorar de maneiras que talvez você não tenha considerado imediatamente.

Paul Turner
fonte
2
POST geralmente significa criar / inserir, enquanto put geralmente significa atualização salvar / atualizar. É assim que é definido no REST, mesmo que não seja como é geralmente usado em HTML.
quer
2
@EvanPlaice os nomes das especificações HTTP PUT como o verbo criar / atualizar (ele usa um modelo de criação + atualização em vez do mais familiar, criar + recuperar + atualizar) e POST é o verbo "processamento de dados", bem como o verbo "anexar" . Roy Fielding, em seu blog, descreveu o POST como o verbo a ser usado quando você não deseja padronizar a operação. O POST adota a semântica "create" quando você considera anexar um novo item a uma coleção de itens. Nesse caso, o Tragedian bateu na unha na cabeça usando o POST para processar ou adicionar um trabalho de impressão.
Rob
@RobY OK, isso faz sentido. Como exemplo, o PUT pode ser usado para representar um SPROC projetado para inserir dados em um banco de dados. Visto que um POST poderia compor as etapas intermediárias e mutações necessárias para coletar / preparar esses dados; O design da operação POST pode mudar ou ser substituído à medida que o design evolui, mas as operações PUT representam o modelo que (idealmente) não deve ser alterado. Eu atualizaria minha resposta, mas esta já faz um ótimo trabalho em explicar a diferença.
Evan Plaice
4

Eu fiz isso antes. Para imprimir um documento, acabei de retornar uma versão em PDF de um recurso. O cliente só precisa enviar uma solicitação GET para o recurso com o cabeçalho Accept application / pdf.

Isso também evita a criação de um novo URI para recursos temporários, como trabalho de impressão. O uso do cabeçalho HTTP também faz parte do REST e mantém o URI limpo.

imel96
fonte
3

Basta adicionar um parâmetro ao GET do URI atual

É bastante típico usar um URI para várias ações.

Se você estiver falando do mesmo recurso, mas de uma ação diferente, você o definiria como um parâmetro.

/ customer / {id}? print = true

Então, onde você define seu método GET, detecta a presença do parâmetro de impressão e o trata de maneira diferente.

REST é definido da seguinte maneira:

  • POST - Crie um registro, ativo ou recurso
  • PUT - Atualização, um registro, ativo ou recurso
  • DELETE - Remover um, registro, ativo ou recurso

GET, por outro lado, deve ser usado de várias maneiras, porque normalmente existem muitas formas diferentes de recuperar um recurso. É também por isso que as solicitações GET são representadas como uma sequência de consultas. Se você estivesse trabalhando com um recurso de banco de dados, estaria literalmente recuperando uma visualização por meio de uma consulta, mas o REST é intencionalmente abstraído para um nível superior porque foi projetado para lidar com muitos tipos diferentes de recursos.

A especificação REST é bastante prospectiva, embora as APIs estejam apenas começando a usá-la recentemente.

Se você estiver interessado em aprender mais sobre os protocolos REST, sugiro que você leia " Aborrecedores que odeiam o ódio ".


Atualizar:

@ Shauna apontou um buraco interessante no meu raciocínio. Não há verdadeiro caminho certo e muitos formulários são considerados aceitáveis. Pensei um pouco mais e, como o uso pretendido é transformar os dados em uma representação diferente, faz sentido definir a transformação como um novo tipo MIME.

Por exemplo, você pode representar o URI como:

/customer/{id}+print

Onde você pode definir a resposta Content-Type como text / html + print. Dessa forma, você também teria a opção de definir mais transformações no futuro.

Por exemplo:

// for application/json
/customer/{id}+json

// for application/atom+xml
/customer/{id}+atom

De qualquer maneira, todas as formas são aceitáveis. A implementação que você decide depende mais da preferência pessoal e dos recursos do seu servidor.

Aparte: Deixe-me esclarecer, pois parece haver alguma confusão. O parâmetro de consulta 'print' e / ou tipo de conteúdo é usado para especificar como o recurso é transformado. Não é como acionar um trabalho de impressão físico. Por motivos de segurança, o acesso no nível do hardware é sempre deixado para o usuário / cliente / navegador.

Evan Plaice
fonte
Para adicionar - Como alternativa ao uso da string de consulta ( ?print=true), você também pode usar parâmetros de URI (ou seja, - /customer/{id}/printable). Qual você usará dependerá amplamente do padrão que seu sistema (CMS, estrutura, código em geral) está configurado para manipular. Ambos são considerados válidos e aceitáveis .
Shauna
@ Shauna Tecnicamente, a melhor abordagem seria empregar um tipo MIME específico para imprimir com o URI '/ customer / {id} + print' e uma resposta MIME-Type de text / html + print. A vantagem dessa abordagem é que você pode criar transformações para muitos tipos MIME (ex text / html, text / x-markdown, application / json, etc) para o mesmo URI. A desvantagem da solução que você apresenta é que você precisará criar um URI adicional (e definir outra rota) para cada tipo MIME diferente. Meio que derrota o propósito de usar o REST.
Evan Solha
(cont) Eu argumentaria que os hackers de URI são um antipadrão introduzido principalmente pela comunidade ROR, mas isso não significa que eles não sejam úteis. Com a chegada de melhores servidores HTTPd de baixo nível, está se tornando mais fácil implementar o REST de uma maneira que aproveita totalmente seu potencial. As coisas percorreram um longo caminho desde os dias em que o Apache e o roteamento de tudo através do index.html eram a única opção.
Evan Plaice
2
O GET não deve fazer alterações de estado ou ter efeitos colaterais. Considere que GET é idempotente, o que significa que o middleware pode repetir a solicitação se ela não for exibida. Nesse caso, cada nova tentativa resultaria em uma nova cópia impressa do documento. ;)
Rob
@RobY Eu estava assumindo que a ação 'imprimir' não lidaria com o processo de impressão física do documento, pois seria melhor atendida pelo navegador e pelo driver de impressão. Em vez disso, a saída de mídia / impressão retornaria uma representação 'amigável para impressão' do documento. Portanto, a idempotência é mantida. Bom ponto, enviar trabalhos de impressão pela Internet de maneira apátrida seria um mau momento.
Evan Plaice