Eu e o Phillip Calçado estamos trabalhando numa aplicação com uma interface de webservices REST.
Um dos pontos que estavamos discutindo hoje é sobre os métodos POST e PUT. Qual deles que representa um INSERT e qual representa um UPDATE? Se você procurar na internet vai perceber que vários sites dizem várias coisas diferentes. Foi quando resolvemos mergulhar no problema e entender qual é a forma correta de se fazer as coisas.
Superficialmente falando, webservices REST são CRUDs (acrônimo para Create, Read, Update e Delete). Por isso é muito natural tentar traçar um paralelo entre os métodos HTTP e as operações de banco de dados feitas por um CRUD
HTTP -> SQL
- GET -> SELECT
- DELETE -> DELETE
- POST -> UPDATE
- PUT -> INSERT
O nosso erro foi justamente esse de tentar traçar um paralelo com as ações de um CRUD. Se você pensar em SQL, realmente faz sentido que o POST seja um UPDATE e o PUT um INSERT. Porém quando se trata de webservices REST a coisa fica um pouco diferente porque HTTP é um pouco diferente de SQL.
O funcionamento do método PUT é “colocar” uma página numa determinada URL. Se a página já existir naquela URL ela é substituída. Se não houver página, ela é criada e passa a ser a que foi enviada no PUT. PUT é uma operação limitada que simplesmente coloca uma página numa localidade. Além disso, PUT é idempotente, ou seja, se você fizer 20 vezes um PUT numa URL o resultado tem que ser o mesmo que seria se você fizesse uma vez só: é o mesmo que acontece se você executar 20 UPDATES em um registro, é o mesmo que executar um update só.
Para exemplificar, considere que estamos criando um usuário num sistema qualquer. Neste sistema, a URL para obter (GET) um usuário de ID 1, por exemplo, seria:
http://gc.blog.br/usuarios/1
Então, se você desejasse criar um usuário utilizando PUT, a URL teria que ser:
http://gc.blog.br/usuarios/[id]
Porém quando você cria um objeto você normalmente não sabe qual é a chave primária/id deste objeto. Quem te dá esta informação é o sistema, não é você que escolhe. Neste caso, se você fizer um PUT numa URL de um usuário que já existe, você o AUTALIZARIA ao invés de criar um usuário novo.
Para criar um usuário o correto seria fazer um POST para a URL:
http://gc.blog.br/usuarios
Ao receber um POST esta URL cria um novo usuário e retorna o ID dele no response. O POST não é idempotente e se esta URL for chamada 20 vezes, 20 usuários serão criados e cada um deles terá um ID diferente: o mesmo que acontece quando você executa 20 INSERTS. Esta URL pode ser considerada como uma Factory de objetos Usuario.
Sendo assim a forma correta de se mapear os métodos HTTP para ações de um CRUD é:
- GET -> SELECT
- DELETE -> DELETE
- POST -> INSERT
- PUT -> UPDATE
Em alguns casos, porém, você sabe qual é a URL que será criada para o usuário. Por exemplo, os seus usuários poderiam ser representados da seguinte forma:
http://gc.blog.br/usuarios/guilherme
Ou seja, para criar um usuário com PUT você poderia chamar uma URL com o padrão:
http://gc.blog.br/usuarios/[login]
Porém esta operação é extremamente arriscada. Se já existir um usuário “guilherme” o sistema achará que você está tentando atualizar o Guilherme ao invés de criar um novo e o “guilherme” antigo irá se perder. Se for um POST o sistema pode retornar uma mensagem dizendo que não pôde criar o “guilherme” porque já existe um usuário com este login.