Para incluir um ID de recurso na carga útil ou derivar do URI

13

Ao projetar uma API, deparamos com a questão de saber se uma carga útil de PUT deve conter o ID do recurso que está sendo atualizado.

É o que temos atualmente:

PUT /users/123 Payload: {name: "Adrian"}

Nosso código de rota extrai o ID do URI e continua com a atualização.

Os primeiros usuários de nossa API estão questionando por que não permitimos o ID na carga útil:

PUT /users/123 Payload: {id: 123, name: "Adrian"}

O motivo de não permitirmos isso é porque o ID é duplicado, na carga útil e no URI.

Pensando nisso um pouco mais, estamos acoplando o recurso ao URI.

Se o URI não tiver o ID, a carga útil precisará ser alterada:

PUT /no/id/here Payload: {name: "Adrian"} < What user???

Há alguma razão para não?

Adrian Lynch
fonte

Respostas:

14

Você deve associar o Identificador Uniforme de Recursos ao recurso .

Quando o REST é implementado com HTTP, você usa GET para recuperar o valor atual do recurso e PUT para definir um novo valor. O GET não possui uma carga útil; portanto, o recurso deve ser identificado pelo URI. E o PUT é feito logicamente no mesmo URI e a carga útil deve ser exatamente igual à que você deseja que o próximo GET retorne.

Você pode usar o POST para URI diferente, mas isso faria menos sentido, pois seria desnecessariamente assimétrico ao GET. O POST para URI comum só poderia fazer sentido para a criação de novos recursos ( POST /users/new, carga útil: {name: "Adrian"}resposta {id: 345, name: "Adrian"}), mas isso não é idempotente e, portanto, deve ser evitado se você está se esforçando para REST¹ Em vez disso, você deve reservar o ID com uma chamada e, em seguida, usar PUT para definir o novo ID; isso é tolerante a falhas, porque, se a primeira solicitação falhar, a reserva de ID poderá atingir o tempo limite eventualmente e ela PUTé idempotente. Ou use o UUID gerado pelo cliente.


¹ A definição de REST não diz nada sobre idempotência, por isso não posso afirmar que não é REST se você tiver operações não-idempotentes. Isso não altera o fato de que atender a solicitações idempotentes torna as coisas mais confiáveis ​​sem complicá-las e, portanto, é recomendado.

Jan Hudec
fonte
3
Tanto quanto sei, uma solicitação POST não precisa ser idempotente. Portanto, não vejo problema em postar em /users(não é necessário adicionar 'novo').
Lex82
Obrigado pela sua resposta. Vejo que é um recurso interessante ter idempotência para todas as solicitações, mas quem diz que isso é necessário para uma API REST? Certamente, muitas APIs que se autodenominam RESTful permitem solicitações POST não idempotentes (especialmente quando o servidor gera identificações para novos recursos). Mas mesmo se você aplicar uma definição muito estrita de REST, não vejo qual restrição de arquitetura é violada com POSTs não idempotentes, como no exemplo acima.
Lex82 22/04
Certamente posso imaginar solicitações POST que violam uma restrição. Só não vejo por que postar um recurso em uma coleção e deixar o servidor decidir sobre seu ID é uma violação das restrições REST.
Lex82 22/04
3
Toda a idéia de POST não é para ser idempotent ....
EralpB
2

Pensando nisso um pouco mais, estamos acoplando o recurso ao URI.

Se o URI não tiver o ID, a carga útil precisará ser alterada:

PUT / no / id / here Carga útil: {nome: "Adrian"} <Qual usuário ???

Há alguma razão para não?

A resposta a esta pergunta depende se você deseja permitir que o cliente altere o ID?

Se o cliente puder alterar o ID, via PUT, o URI do recurso será alterado, e você deverá fornecer um 301 Movido Permanentemente sempre que um recurso acessar o URI antigo.

Por exemplo, você começa com um recurso em

/users/123

e o cliente coloca o seguinte no recurso

{id: 222, name: "Adrian"}

o recurso foi atualizado e seu URI está agora

/users/222

O Locationcampo na resposta PUT deve conter o novo URI e, se você for, /users/123deverá obter uma 301resposta com o campo Localização apontando para o novo /users/222recurso.

Na maioria dos casos, na verdade, você não deseja que o cliente possa alterar o ID, pois isso pode ficar muito confuso rapidamente. Nesse caso, o ID é algo que apenas o servidor pode alterar e você deve deixá-lo fora do corpo PUT, pois o cliente não pode atualizar esse estado.

Se você colocar um requerimento para um URI diferente no mesmo recurso, diga

/users/adian_lync

então, se esse recurso não existir, o servidor deve criá-lo e criar e ID quando estiver fazendo isso

Cormac Mulhall
fonte
1
O motivo para questionarmos o posicionamento do ID na carga útil foi devido ao fato de o Backbone.js transmitir o ID em uma solicitação PUT por padrão. Podemos impedir que isso aconteça, mas agora quero saber por que esse é o comportamento padrão.
Adrian Lynch
1
Com medo de não estar familiarizado com o Backbone.js. Parece desnecessário se o ID também estiver incluído no URL. Talvez um descuido por parte dos devs
Cormac Mulhall