Estou desenvolvendo uma API RESTful e estou com um problema com a operação “excluir bloco”, o que era para ser um simples DELETE se tornou um problema, há um requisito que exige que seja informado o motivo da exclusão.
[DELETE]
…/blocos/12
payload: “Motivo da exclusão”
Estava tudo lindo, a API ficando bacana, mas na hora de implementar descobri que o RESTEasy não dá suporte a DELETE com payload.
Alguém já passou por problema parecido? Há alguma forma de fazer um DELETE com payload? Caso não seja possível qual a forma mais elegante de lidar com uma situação dessas?
Acontece que dentro das melhores práticas para construção de RESTFul APIs os query parameters são utilizados para passagem de parâmetros opcionais. por exemplos os filtros.
[GET]
…/usuarios?status=ativo&sexo=masculino
No caso o motivo da exclusão é um parâmetro obrigatório.
@RequestMapping(value = "/blocos/{id}", method = RequestMethod.DELETE)
public HttpEntity<Void> delete(@PathVariable Long id, @RequestBody Motivo motivo) {
//Sua lógica para exclusão. Lembrando que caso falhe tanto a persistencia do motivo quanto a exclusão do bloco
// Você deve desfazer as transações e retornar um BadRequest.
}
Tava pensando nessa falha no contexto do Motivo vir com um problema de consistência ou no caso do bloco que deve ser deletado possuir alguma referencia que impeça a exclusão, por isso um BadRequest que poderia até ir como uma mensagem explicativa para o frontend. No caso do error 500, eu considero uma exceção que não foi tratada.
Como estou usando o RESTEasy não vou adicionar mais essa dependência do Spring, o que provavelmente causaria problemas. Em alguns fóruns percebi que esse problema(do DELETE) é comum e que realmente algumas implementações não preveem o DELETE com payload, você pode declarar mas ao consumir o serviço dará erro.
Ainda pesquisando descobri que poderia fazer um POST sem me sentir tão culpado, que existem situações que o negócio vai permitir essas situações.
Correto. E foi o que eu entendi por falha na persistencia ou falha pra logar o motivo.
Não tem razão pra retornar BadRequest quando a exclusão não pode ser feita por uma questão interna do sistema. BadRequest é um erro que está relacionado à forma como o request foi construído pelo cliente.
Se não pode excluir um recurso por questão lógica e de consistência de dados, então obviamente sua aplicação não deve oferecer essa opção na GUI, e se alguém tentar fazer isso forçando o caminho, o correto seria retornar Forbidden (403).
O PUT pelo que já li a respeito deve ser usado quando você atualiza alguma informação do objeto e o resource não é alterado, exemplo:
[PUT] …/bloco/12
{
“nome”:“bloco de teste”
}
O bloco continuará disponível no caminho anterior, mas com as atualizações feitas.
O ideal para minha API seria:
[DELETE] …/bloco/12
{
“motivo”:“Bloco mal gerado”
}
Mas o RESTEasy não me permitiu, por particularidades da implementação dos caras.
Como solução resolvi fazer uma interpretação diferente do cenário e adicionei o bloco ao resource dos cancelados, para inclusões e adições o método utilizado é o POST.
POST não é idempotente. Isso significa que o servidor vai criar uma nova URL a cada bloco excluido, e mandar o cliente pra lá.
Mas o que você quer é apenas incluir todos os excluídos em uma url já conhecida pelo cliente.
O legal dessa dúvida, que está gerando uma discussão legal sobre HttpVerbs, HttpResponses e agora Idempotence, me desculpe pela visão simplificada ou caso eu esteja errado.Mas o put e o delete são considerados idempotentes, porém o conceito idempotente, não se resume ao fato de uma mesma request ser repetida “n” vezes e produzir o mesmo resultado? No caso do delete, a primeira request eu retornaria 200 ou 204, ao repetir a request eu teria um 404. Isso não fere o principio do idempotente?
Idempotente se refere ao request e como ele altera o estado do sistema, e não a resposta que você recebe.
Pode chegar 1 DELETE ou vários ao mesmo tempo, você sabe que o resultado produzido no sistema é o mesmo, ainda que alguns clientes recebam respostas diferentes.