0

I have an url like this:

http://www.example.com/users/

I want to create a new user with a PUT request:

requests.put('http://www.example.com/users/john/', data={'password': 1234})

Actually this request creates a new user resource and returns a 201 "Created" status code, if i want to update that resource i can make a request like:

requests.put('http://www.example.com/users/john/', data={'username': 'jane'})

Well, i can return a response with a 301 "Moved Permanently" status code and the new created url in the Location header. The problem comes when i am trying to implement such logic in the controller code, in some part of the logic i have some code that checks for the existence of the username, something like:

if username in users:
  # do something

If i use PUT for create a new resource, in this case an user, saying that the username already exists i could return a 409 "Conflict" status code with a message like "Username already exists, choose another one", if the username doesn't exists i just return a 201 "Created", logically this creation operation not require authentication cause is like a "sign up" operation.

In the other hand when i request for update the resource i need to do some kind of authentication in order to allow an user to update his account information.

So if i use the username variable to determine what operation i have to perform, and the username name already exists, i can't know when to respond with an authentication required or an invalid username response.

My first thought was to use a POST request to create a new user and PUT just for the update behavior, but it seems that use POST for creating a resource who's identifier is not created by the server is a bad idea.

So my question is if there are any way of implement this both create and update functionality with a PUT request?

angvillar
  • 1,074
  • 3
  • 10
  • 25

1 Answers1

1

For update, another option is PATCH and you could authenticate that request and treat that as a pure update.

When you create an object, you could created it against the collection and return 201. When you fully replace the complete resource, you could PUT against the user resource. PATCH could be for partial update.

Create (Anonymous)
Request  : [POST|PUT]  /users/        body=full object (incuding name)
Response : 201 LOCATION =  /users/123

Overwrite (Auth)
Request   : [PUT]      /users/123     body=full object - id implies overwrite    
Response  : 200

Update (Auth)
Request   : [PATCH]    /users/123     body=partial objects      
Response  : 200

By creating the resource (via POST | PUT) against the parent resource (users collection in this case). It solves the problem. It's also good practice. You should also create resources with permalinks (int id or guid) so a user can change their name and not have stored references break.

If you want the identifier to be a permalink and you want it created by the client (as suggested in a comment) then you would need to use something like a guid for the identifier.

bryanmac
  • 38,941
  • 11
  • 91
  • 99
  • i don't want to use POST over /users/ because it means to me that the id for the resource user will be server generated as posted in http://stackoverflow.com/questions/630453/put-vs-post-in-rest, if i use PUT over /users/ i undertand that i want to update the whole users resource, maybe a good solution will be create user with PUT on /users/john/ and update partial account information with PACTH /users/john/ but if i want to update the whole /users/john/ resource via PUT i will encounter the same problem. – angvillar Mar 04 '13 at 23:14
  • Can you clarify whether by id you mean an int|guid identifier or do you mean 'John'? Also, do you want the server or client to generate that id? – bryanmac Mar 04 '13 at 23:27
  • i mean that if i use POST over /users/ i will expect that the server generates a new id for my resource, maybe a database id so the response after that POST operation will contain some like /users/3281/ in the Location header – angvillar Mar 04 '13 at 23:32
  • I agree that if the intent is to completely overwrite and resplace then you want to PUT against that resource (users/123). But, I think it's OK if you're creating a resource, to do it against the collection. I updated the answer. By using the identifier, it is a permalink even in the case where the user is renamed. Hopefully I understood you're requirements. – bryanmac Mar 04 '13 at 23:48
  • i agree that use PUT to overwrite a resource and PACTH to partial update is quite good, to create resources i would choose PUT over /users/john/ because i am specifying the resource new url, but i still having the problem of determine when to know what is the purpose of an incoming PUT request over /users/john/ that requires auth for overwrite and not require auth for creation. – angvillar Mar 05 '13 at 00:03
  • See: http://stackoverflow.com/questions/2810652/how-to-design-a-restful-collection-resource – bryanmac Mar 05 '13 at 00:07
  • Read the prev link - by creating against the parent resource (collection) it solves this problem. It's still best practice to use a permalink identifier but even if you want the username to be the identifier, you can still specify it via the client if that's your wish by the object you POST|PUT – bryanmac Mar 05 '13 at 00:11
  • Instead of other solution, it seems that create a new user resource via POST on /users/, passing username and password in the request body, and return a response with the Location header as /users/123/ or /users/john/, and then using PUT just for overwrite the resource will work, but i still missing the point of create the resource via PUT as Marcelo Cantos says in 1.1 You could use a PUT if the client has the right and ability to generate the name or id of the resource. That is the point here, because the client, a given user, has the ability to generate the name of the resource. – angvillar Mar 05 '13 at 00:29
  • Nothing in the outlined solution above prohibits the client from (1) creating the name of the resource (John) or (2) even the identifier if you want that. But if the client creates the identifier, of course it can't be an auto incrementing int. You would need it to be a guid or something the client can guarentee to be unique. – bryanmac Mar 05 '13 at 01:03