Skip to main content

Web: Redirecting the User Back After Some Workflow

Here's a common Web application workflow. A user gets to a page. He doesn't have access to the page because he isn't logged in. He gets redirected to the login page. He logs in. Perhaps he even has to create an account first. Afterwards, he gets redirected to the page he was trying to go to in the first place. Users find this sort of behavior helpful.

Here's another. The user is trying to buy something. He gets redirected to Google checkout (disclaimer: I've never used Google checkout). After he finishes checking out, he gets redirected back to the origin site. Perhaps he now has access to download the thing he just bought.

Thanks to the authlogic tutorial, I already had code to take care of the login workflow. However, I now find myself needing to do more workflows like it. The user tries to go to a page. However, he must do this other thing. Afterwards, he can go to the original page. What's the best way to handle the problem generically?

Here are the constraints that come to mind. I don't want two workflows stepping on each others toes. (I'm not even sure workflows have toes!) Hence, if one workflow leads to a page, and then the user has to do another workflow, he should get redirected at the end of the inner workflow and then get redirected at the end of the outer workflow. However, I don't expect that the user will need to recurse. That is, I don't expect one workflow to lead to itself again in a recursive sort of way.

With all that in mind, I took what authlogic gave me and expanded on it. In my base class, ApplicationController, I have:
  protected

def store_location(session_key_suffix, url=request.request_uri)
session[session_key_for_return_to(session_key_suffix)] = url
end

def redirect_back_or_default(session_key_suffix, default)
key = session_key_for_return_to(session_key_suffix)
redirect_to(session[key] || default)
session[key] = nil
end

def location_stored?(session_key_suffix)
session.has_key?(session_key_for_return_to(session_key_suffix))
end

private

def session_key_for_return_to(session_key_suffix)
"return_to_#{session_key_suffix.to_s}".to_sym
end
Here's how to use it to handle the login case:
  def require_user
unless current_user
store_location(:after_login)
flash[:error_message] = "You must be logged in to access this page"
redirect_to new_user_session_url
return false
end
end
After the user logs in, I call:
  redirect_back_or_default(:after_login, root_url)
Based on my tests, the above works. It can handle nested workflows. It can even handle a user working on two different workflows in two different tabs. I can envision some esoteric ways to mess the above code up, but I'm going to give it a couple weeks and see how it turns out in practice.

Comments

Storing such URLs in session has one major flaw: if you work in two tabs, than you share session and might end redirecting user in the wrong tab.
jjinux said…
I know. The web is a pain. At least if they're working on two different workflows, the above code will work.
jjinux said…
And if you're hinting at the idea of storing the URL to return to in the URL, the main reason I'm against this approach is that it's very painful to do once you have workflows that take several pages, and you have multiple of them happening at the same time. Not impossible, but very hard.
jamesd said…
I had a similar problem and am using a hidden form input.

So call "login/?return_to=comment" and get username and password from the user but pass in "comment" as the place we want to come back to.

I'm new to programming but that seems to work pretty well.
jjinux said…
cool

Popular posts from this blog

Ubuntu 20.04 on a 2015 15" MacBook Pro

I decided to give Ubuntu 20.04 a try on my 2015 15" MacBook Pro. I didn't actually install it; I just live booted from a USB thumb drive which was enough to try out everything I wanted. In summary, it's not perfect, and issues with my camera would prevent me from switching, but given the right hardware, I think it's a really viable option. The first thing I wanted to try was what would happen if I plugged in a non-HiDPI screen given that my laptop has a HiDPI screen. Without sub-pixel scaling, whatever scale rate I picked for one screen would apply to the other. However, once I turned on sub-pixel scaling, I was able to pick different scale rates for the internal and external displays. That looked ok. I tried plugging in and unplugging multiple times, and it didn't crash. I doubt it'd work with my Thunderbolt display at work, but it worked fine for my HDMI displays at home. I even plugged it into my TV, and it stuck to the 100% scaling I picked for the othe

ERNOS: Erlang Networked Operating System

I've been reading Dreaming in Code lately, and I really like it. If you're not a dreamer, you may safely skip the rest of this post ;) In Chapter 10, "Engineers and Artists", Alan Kay, John Backus, and Jaron Lanier really got me thinking. I've also been thinking a lot about Minix 3 , Erlang , and the original Lisp machine . The ideas are beginning to synthesize into something cohesive--more than just the sum of their parts. Now, I'm sure that many of these ideas have already been envisioned within Tunes.org , LLVM , Microsoft's Singularity project, or in some other place that I haven't managed to discover or fully read, but I'm going to blog them anyway. Rather than wax philosophical, let me just dump out some ideas: Start with Minix 3. It's a new microkernel, and it's meant for real use, unlike the original Minix. "This new OS is extremely small, with the part that runs in kernel mode under 4000 lines of executable code.&quo

Haskell or Erlang?

I've coded in both Erlang and Haskell. Erlang is practical, efficient, and useful. It's got a wonderful niche in the distributed world, and it has some real success stories such as CouchDB and jabber.org. Haskell is elegant and beautiful. It's been successful in various programming language competitions. I have some experience in both, but I'm thinking it's time to really commit to learning one of them on a professional level. They both have good books out now, and it's probably time I read one of those books cover to cover. My question is which? Back in 2000, Perl had established a real niche for systems administration, CGI, and text processing. The syntax wasn't exactly beautiful (unless you're into that sort of thing), but it was popular and mature. Python hadn't really become popular, nor did it really have a strong niche (at least as far as I could see). I went with Python because of its elegance, but since then, I've coded both p