1

I have a REST Service based on Spring MVC. JSON is the only interchange format for any client to talk to this service.

I'm working on implementing a user registration workflow. So I have created an endpoint (/register, payload=User object in JSON) that accepts the User entity and does it's thing. So I'm transferring the state of an object which makes me content about adhering to the REST philosophy (I think).

My question is: Before I can register a user, I need to check if ther username is already used up. So I can create a new endpoint /register/username/ But this idea sounds bad and not RESTful at all as I'm using the endpoint to perform an action, not transfer of state. And this sounds like I'm invoking a method to perform an action (remote RPC). How can I re think this in terms of REST?

asked Oct 21, 2015 at 21:13

4 Answers 4

6

I'm using the endpoint to perform an action, not transfer of state.

Actually, you are transferring state. The client is sending the proposed new user entity to the server. The server has its own rules to enforce, however, so it must refuse to create a user if their unique identifier already exists.

There is already an HTTP error code for that. As @tom pointed out, the 409 Conflict response code is acceptable for that. It basically means What you asked me to do is perfectly legal in from your standpoint. I am simply unable to fulfill your request because it is in conflict with something outside of your control versus some of the other response codes which focus on inappropriate client requests.

As far as your concern about using REST to perform an action in this case, I don't agree, but I am glad you are thinking about that. I have seen developers create "REST services" named things like GET /whatever/updateWidget, which is clearly not a RESTful way of thinking.

You are performing an action only in the sense that you are creating a new resource. But that resource is based on the state you transferred. You have to create it somehow.

As long as you return a meaningful error code like 409, I do not see any problems with your approach whatsoever.

answered Oct 22, 2015 at 0:27
3
  • 2
    Thank you. If I return 409 Conflict, How can the client decipher if the conflict occured because the username is already used up or if the password criteria is unacceptable to the Server?? How can the server construct the response so that the client gets the necessary information it needs to present to the user?: Commented Oct 22, 2015 at 4:26
  • Renaming the endpoint from "register" to "users" should be enough, right? Commented Oct 22, 2015 at 6:05
  • 5
    @user1324816: 409 expects you to add to the response body "enough information for the user to recognize the source of the conflict". Commented Oct 22, 2015 at 6:14
2

When a client attempts to add a duplicate, I would return a 409 Conflict. A 400 bad request (with explanation error in response) would also be fine and is probably more common practice.

However, the jury is out on this one. REST doesn't make it clear what to do in these situations. See https://stackoverflow.com/questions/3290182/rest-http-status-codes-for-failed-validation-or-invalid-duplicate

answered Oct 21, 2015 at 23:09
2

For starters, and I think you're onto this already, don't invent new verbs and stuff them into the tree. If you find yourself wanting to do that, stop and ask yourself where in the model the data should fit, then POST it there.

Creating a new user should be an operation you do against the same part of the tree you use to get at the ones that already exist. For example, you'd GET /users/lanceboyle to retrieve Lance Boyle's user information, so you should be POSTing to the same place to create a new one (e.g., POST /users/duanepipe).

Your situation seems to hit the major points for a return code of 409. From RFC 7231 (emphasis mine):

The request could not be completed due to a conflict with the current state of the resource.

If there's already data in /users/warrenpease, then its state is "existing." Trying to POST a second copy to the same location conflicts with that state because you can't create a new one of what already exists.

This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request.

Again, the user (requester) can resolve this problem by picking a different user name and POSTing it to a location that doesn't exist.

answered Oct 22, 2015 at 0:37
0

If you want the user name uniqueness check to be performed as part of creating an account, you could let "POST /accounts" return 409 if an account for that user name already exists. See the overview of HTTP status codes. In the response body, you could return an appropriate error message. If you have a convention for specifying exactly which field is invalid in the error response, then you could use that convention to indicate that the user name is causing a problem (e.g. to highlight that field on the screen). Example 409 response:

{
 "message" : "Unable to register new account",
 "errorFields" : [
 { "field" : "userName", "message" : "An account with this user name already exists" },
 { "field" : "password", "message" : "Password too weak" }
 ]
}

However, I am rather thinking in a totally different direction. It is common in such situations, that the client needs to check explicitly for a duplicate user name before attempting to create an account, for example as soon as the user leaves the user name field. In that case, the client app should use the HTTP method 'HEAD'. See the HTTP methods spec. The server returns 200 if an account for that user name already exists and 404 if the account does not exist.

Suppose /accounts represents the collection of accounts and /account/xxx represents an individual account for user name xxx, then the client should do HEAD /accounts/xxx to check if user xxx already has an account. Ideally, the client does not simply append "/xxx" to "/accounts", but uses a templated link, /accounts/{username}, provided by another resource.

I would not put user names in the path, though. I would use technical keys instead, e.g. /accounts/accountID represents an individual account, where accountID is a technical key. In that case, if the client app has to check if user name xxx is already in use, it can do HEAD /accounts?userName=xxx (again, another resource should provide a templated link to do this).

answered Nov 2, 2015 at 16:53

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.