Search |
||
The pedantic guide for a RESTful Registration Use CasePosted by felipegaucho on October 2, 2009 at 3:15 AM PDT
Registration is one of most implemented use cases ever, but things get a bit different when you try to implement it in a RESTful Web-Service. Before I start the discussion about the registration use case, a list of terms for disambiguation:
The Registration Use CaseThe registration use case I am taking as example is the one I just implemented in the PUJ Project - a classical Registration User History adapted to the REST terminology:
The above use case has several variations, and I omitted the exceptions for the sake of clearness. The important points for our discussion are highlighted in bold font and explained below. Confirmation e-mails are not RESTA Natural Born RESTafarian would realize by a glance the issue here, but if you are not that updated with the HTTP Protocol and the HATEOAS constraints, here comes the problem: GET that will change the status of his
account from 'NEW' to 'ACTIVE'. This status change should never be
done through a GET method if you want your Service API to continue to be
called REST.You can take a rest now, a coffee, take a while to digest the idea that most of the confirmation emails out there are not REST. Doesn't matter how beautiful your Web-Service is, if an end user clicks on a GET URL to confirm his registration, your efforts were in vain - your API is not REST. Flames aside, the simple fact is that e-mails containing a confirmation URL is the de facto standard solution for the registration use case on the web. Due to that reason I adopted the word pedantic in the title of this blog, because most of the developers just ignore that detail and go on with the widely adopted solution. I will keep the pedantic mood for now, and the rest of this post is to explain you the workaround I adopted to keep the chances to call my service as RESTful API. I will start enumerating the options you have once you realize the conflict between the REST constraints and the GET URL in a confirmation e-mail:
Some interactions between an End-User and a RESTful Web-Service should be intermediated by a Client ApplicationThe problem of our previous Use Case is a GET method changing the
state of an account resource ( A RESTful version of the Registration Use Case
Notice that only two things actually changed from the original use case:
Benefits of the mediator in the Registration Use Case
The idea of a mediator may be adopted in several other scenarios, just thing a few minutes about that. Try it onlineYou can try the RESTful Registration Use Case on my online demo, just do that:
Notice you are sending a POST Request to the address Are you using an Operational System without the CURL tool? Perhaps this link can can help you. SummaryREST is a good architecture style, sometimes it imposes limitations on what you can do, specially if you consider that new protocols are coming to the game soon: SMS, SMTP, Digital TV ones, etc. The HTTP protocol should be preserved to keep the web clean and robust as we know it. if you abuse the HTTP Protocol due to commercial benefits, it is important to make it clear that you are doing that for a reason and more important: to accept that if you abuse any protocol you are not using the protocol anymore. It may sounds too pedantic, or not, and I leave a large space here for your considerations and eventually for proposing a better solution. We are all here to learn, in this times of flames it is good to spread this motto ;) My self-education on the REST technologies are still in the beginning and I am thankful for all of those sharing their knowledge about that. »
Comments
Comments are listed in date ascending order (oldest first)
How to synchronize Client & Server ?
Submitted by felipegaucho on Fri, 2009-10-02 08:27.
Hi mbinette, > It would be difficult for the email to be customized properly (different "Thank you" message for each client). I am also passing these messages from the Client Application to the Service. Actually, the client provides all resources the service needs to send the message. If I find time to blog about the implementation details, it will be more clear. > The client would then send the email with the exact contents you have in your final solution. The problem is: how do you assure the client sends the email after creating the account record in the database. In my current code, the both actions are executed in a same try-catch block, so if the SMTP fails, the JPA operations is rolled back, and I avoid the situation of having a new account in the database without the delivery of the confirmation email. If you have this orphan record without the confirmation email, you are in a bad situation because the server will not accept a second registration request :) But if you can guarantee client and server are in synch, no problem. Undelivered confirmation
Submitted by mbinette on Fri, 2009-10-02 08:50.
"The problem is: how do you assure the client sends the email after creating the account record in the database. In my current code, the both actions are executed in a same try-catch block, so if the SMTP fails, the JPA operations is rolled back, and I avoid the situation of having a new account in the database without the delivery of the confirmation email.
If you have this orphan record without the confirmation email, you are in a bad situation because the server will not accept a second registration request :)" I guess I was thinking that if the client sent the request for a second registration and the account was not yet active, it would treat it as if it was the first time request, return a 200 with the same hash (or a 201 with the same hash?). That way the user who didn't get the email could retry without it adding new registrations and without it rejecting. Once the account has been accepted, trying to register again with the same email would fail. humm.. good idea
Submitted by felipegaucho on Fri, 2009-10-02 09:05.
Good idea, I will adopt it :) For now, my code first check if the login or the email are already registered in the database and rejects the registration (HTTP CONFLICT).. This may lead a more safe relationship between client and server, and even unleash this use case of being done all on the server side. But: there is a last argument to do it on the server side: to reuse the same code between the several clients consuming the service. A kind of "service resource". Otherwise, all clients will need to implement the email feature (or other confirmation feature). Since all clients will need that, perhaps it is a good reason to keep it as resource instead of required plugin :) I liked your idea and I will implement it in my server side asap. Thanks. great
Submitted by mbinette on Fri, 2009-10-02 09:19.
Maybe the sending of an email should be a separate service? Then the client can take what is returned from the registration service and send it to the email service if they don't want to handle it themselves? Not sure but thanks again for the posts. They help me get out of my analysis paralysis ;) I look forward to your next post.
perhaps not a good idea :)
Submitted by felipegaucho on Fri, 2009-10-02 22:53.
I reviewed my opinion about this.. and I started to think about the client application. It is not easy to handle all problems during the registration, and it gets worse if you do it in two different processes. So I rolled back my mind to the original solution: a unique transaction with "commit & email". To do the two operations in a unique transaction makes the whole use case simpler. Reason: on the GUI we have two key fields in the registration form: login and email. As soon the user type the login, an AJAX function checks if the login is available on the server side. Assuming an end user can resend several times the same information sounds elegant but causes a side effect where I don't know what to do if a login already exists, or an email already exists or both. At first moment I thought about to update the information if the user type a same login & email but with different values in the other fields.. it is ok, but then I started to realize the cases where login or email do not match the database values.. and then we have a complicated permutation of values and a sophisticated code to support it on the GUI.. Due to that, I prefer to keep it as original designed: if a login or an email are already registered in the database, a new registration is not allowed. I will include a method to resend the email, just that - a separated one, because in my opinion it is a different use case (recover password). open for suggestions on this topic. OK
Submitted by mbinette on Sat, 2009-10-03 09:43.
I think there is a difference in how you would handle an activated account and one that has not yet been activated. When you do your AJAX request to check a login (passing both the login name and email addresss, even if one is currently blank), the login name would be considered available until it has been activated. Then when an ajax call is made with the login name AND email, it would continue to say that combination is available if the combination matches what is in the database and it is not activated (the client can treat it as new even though it isn't). If it is already activated then it would say the login/email combination is already in use and it would make visible a "Recover Password" link. If one value matches and the other doesn't, the AJAX call returns that the login is not available. It would all be handled by the service so the client would not get overly complicated. It only needs to know whether the login is, based on the current information, valid, invalid, or exists already.
I don't really see an issue with either way of doing it and I don't think one is any better than the other. I think you might lean towards your final solution when you don't need to worry about branding too much or the possibility of multiple ways of activation from various clients. It simplifies the client, web service, and as a side affect, simplifies security as well. That's not exactly what idempotent means
Submitted by cayhorstmann on Mon, 2009-10-05 08:05.
Idempotent means "if you do it twice, it's the same as doing it once". (http://mathworld.wolfram.com/Idempotent.html) http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.2 explicitly mentions the possibility of side effects, so "idempotent" is not the same as "no side effects".
So, technically, wouldn't a confirmation link be idempotent? If a user confirms twice, it's the same as confirming once. (Unless you want the second response to be "Hey you dummy...you already confirmed!"...) But let's say the response is simply "Thank you for confirming..." What is not RESTful about such a confirmation link?
GET cannot change the state of a resource
Submitted by felipegaucho on Mon, 2009-10-05 09:34.
The problem is: before the GET an "user" resource has a set of attributes in one state, and after the GET the same record changed in the database. The information associated to the resource is not the same and the different is a direct effect of the first GET call. It is not about the response message, it is about what happens when the server side processes the GET Request - a database change is not idempotent.
|
||
|
|
Step 3c
In step 3c of your final solution, the web service sends the email. I'm having a hard time deciding if I like that or not, especially if the registration service gets used by multiple clients, possibly for multiple different applications. It would be difficult for the email to be customized properly (different "Thank you" message for each client).
Is there a way for the client to send the email and not fall into the trap of what you wrote in solution possibility #2, Delegate the security responsibility to the client?
Is there any value that could be returned by the original POST to the web service that the client could then send in a confirmation email (and still stay secure)? The web service would still be checking the values such as name and password and then returning a 200 with a hash of an acceptance string. The client would then send the email with the exact contents you have in your final solution.