Qual é o melhor padrão para adicionar um item existente a uma coleção na API REST?

23

Estou projetando uma API REST pragmática e estou um pouco preocupado com a melhor forma de adicionar entidades existentes a uma coleção. Meu modelo de domínio inclui um projeto que possui uma coleção de sites. Esse é um relacionamento estrito de muitos para muitos e não preciso criar uma entidade que modele explicitamente o relacionamento (por exemplo, ProjectSite).

Minha API permitirá que os consumidores adicionem um site existente a um projeto. Onde estou me desligando é que os únicos dados que realmente preciso são ProjectId e SiteId. Minha ideia inicial foi:

1. POST myapi/projects/{projectId}/sites/{siteId}

Mas eu também pensei em

2. POST myapi/projects/{projectId}/sites

com uma entidade do site enviada como conteúdo JSON.

A opção 1 é simples e funciona, mas não parece muito certa, e eu tenho outros relacionamentos que não podem seguir esse padrão, portanto adiciona inconsistência à minha API.

A opção 2 parece melhor, mas leva a duas preocupações:

  • Devo criar um site ou lançar uma exceção se um novo site for publicado (SiteId = 0)?
  • Como eu só preciso do ProjectId e do SiteId para criar o relacionamento, o Site pode ser postado com dados ausentes ou incorretos para outras propriedades.

Uma terceira opção é fornecer um terminal simples apenas para criar e excluir o relacionamento. Esse terminal esperaria uma carga útil JSON contendo apenas ProjectId e SiteId.

O que você acha?

Jamie Ide
fonte
2
Veja também: stackoverflow.com/questions/2001773/…
Rory Hunter
@RoryHunter Há alguma discussão interessante nesse link, mas nada que elimine minha incerteza. Eu gosto especialmente que a resposta aceita declare "Você entendeu direito". e o 2º lugar (embora por uma grande margem) responde "Simplificando, você está fazendo isso completamente ao contrário".
21314 Jamie Ide
Sua primeira opção é boa, embora eu usasse PUT em vez de POST, pois o cliente está no controle da identidade que está sendo adicionada à coleção. Sua primeira preocupação com a opção 2 é inteiramente sua, se você não quiser novos sites, não crie uma exceção, mas retorne um dos códigos 4xx. Sua segunda preocupação não está aqui nem ali. Você não deve postar um site inteiro, a menos que permita adições. A adição de um site existente deve ter o ID apenas quando você está modificando o site, mas apenas a coleção "ProjectSite" (mesmo se você não criar um recurso separado para ele).
Marjan Venema

Respostas:

14

POST é o verbo "acrescentar" e também o verbo "processar". PUT é o verbo "criar / atualizar" (para identificadores conhecidos) e quase parece a escolha certa aqui, porque o URI de destino completo é conhecido. projectIde siteIdjá existe, então você não precisa "POSTAR para uma coleção" para produzir um novo ID.

O problema com o PUT é que ele exige que o corpo seja a representação do recurso que você está colocando. Mas a intenção aqui é anexar ao recurso de coleção "projeto / sites", em vez de atualizar o recurso do site.

E se alguém colocar uma representação JSON completa de um site existente? Você deve atualizar a coleção e atualizar o objeto? Você poderia apoiar isso, mas parece que essa não é a intenção. Como você disse,

os únicos dados de que realmente preciso são ProjectId e SiteId

Em vez disso, eu tentaria postar a siteIdcoleção e confiar na natureza "anexar" e "processar" do POST:

POST myapi / projects / {projectId} / sites

{'identidade': '...' }

Como você está modificando o recurso de coleção de sites e não o recurso de site , esse é o URI que você deseja. O POST pode saber "anexar / processar" e adicionar o elemento com esse ID ao conjunto de sites do projeto.

Isso ainda deixa a porta aberta para a criação de novos sites para o projeto, aprimorando o JSON e omitindo o ID. "No id" == "criar do zero". Mas se o URI da coleção obtiver um ID e nada mais, ficará bem claro o que precisa acontecer.

Pergunta interessante. :)

Roubar
fonte
Eu acredito que o POST é para criar e o PUT é para atualização, mas sua conclusão é onde eu acabei ontem. O bom é que, graças ao roteamento de atributos na API da Web, eu tenho o código em um controlador ProjectSites para que o código esteja bem organizado.
21314 Jamie Ide
Eu acho que a razão que você precisa usar em POSTvez de PUTou PATCHaqui é que você não tem toda a Siteentidade para colocar no sitesrecurso. Você tem apenas o ID, que requer processamento para adicioná-lo à coleção.
crush
4

Usamos o Patchmétodo para coisas assim. O que você deseja fazer é modificar um projeto existente para adicionar um site a ele.

Então, algo assim funcionaria

PATCH myapi/projects/{id} 

com a entidade Site (s) como JSON / JSONArray no corpo da solicitação.

Dessa forma, você pode usar a mesma URL para modificar partes diferentes do Projeto, se necessário - seu código na implementação deve ser inteligente o suficiente para lidar com essa modificação parcial do recurso.

juan
fonte
Abordagem interessante. Eu tenho um modelo de domínio mais antigo "rico" (ou seja, altamente dependente) e o Project especialmente tem muitas coleções penduradas nele. Detectar o tipo de entidade que está na solicitação seria um desafio e não se encaixa no meu objetivo pragmático.
Jamie Ide
Por que um desafio? Se você tiver essas restrições, sempre poderá usar um JSON que torne explícito o que está enviando ... assim {"sites": [], "other-stuff": {}}, poderá ramificar seu código para lidar com todos esses "sujeitos" com muita facilidade. Realmente depende do seu problema específico, mas eu ainda recomendaria o uso do PATCH, pois ele foi projetado especificamente para esse tipo de coisa.
juan
As desvantagens que vejo são: 1) a API não comunica explicitamente quais coleções permitem alterações; 2) não pode alavancar a ligação de parâmetros da API da Web; 3) interruptor grande ou se declaração.
21314 Jamie Ide
Eu nunca vi o método de correção utilizado em qualquer outro lugar
Nim Chimpsky
PATCHTambém não esperaria que a entidade completa fosse passada como seu valor aqui, em vez de um ID que aponte para alguma entidade?
crush