I need to make the client to navigate through a series of redirects. After the user arrives to the destination, I'd like to allow the user to go back to an intermediate page to be redirected elsewhere.
For example, take the following diagram:
Current Page -> Processing Page -> Landing Page
Status: 3XX
|
V
Alternative Page
Disclaimer: I do not have control over Landing Page but I do have control over the others.
From the Current Page, the user is sent to the Processing Page which, after checking the DB, redirects the user to the Landing Page. What I would like is, if the user presses the back button, to be sent back to the Processing Page so it can redirect the user to the Alternative Page.
The problem is that, because of the 3XX status code, the Processing Page is never injected into the browser's history so when the users goes back, they are sent to the Current Page directly.
So far, I've achieved my goal by making Processing Page to return 200 and force a redirect via JS as the first thing but it feels like a clunky solution.
Would it be possible to achieve the same outcome with a combination of HTTP headers? Another solution, since I have control over Current Page is to place the decision making algorithm there but this is a complex enough page already that I'd rather prefer to avoid this option.
Many thanks!
Related
This question is a question about login flows for web-apps in general. I'm most interested in answers that optimize for usability and performance while maintaining security.
What is the most appropriate way to handle unauthenticated requests to bookmarked URLs?
To demonstrate the problem, here are some routes and respective behaviors for an example application:
GET /login -> Display the authentication form
POST /processLogin -> process the username and password,
if unauthentic...re-render the login form;
otherwise...display the default page
GET /secret -> if authenticated...display the secret resource;
otherwise...display a login form
POST /secret -> if authenticated...perform a desirable, but potentially
non-idempotent action on the secret
resource
otherwise...display a login form
Option 1: Display login screen, redirect to desired page
User clicks bookmark
GET /secret -> 200, surreptitiously display login form with hidden field path="/secret"
POST /processLogin -> 302 to /secret (value of path parameter)
GET /secret -> 200, secret resource displayed
Analysis: Hopefully, your client is a modern browser, non-compliant with HTTP, such that it performs a GET after a 302'd POST. This applies across the board. Should I be worried?
Option 2: Redirect to login screen, redirect to desired page
User clicks bookmark
GET /secret -> 302 to /login
GET /login via redirect -> 200, login form displayed with hidden field path="/secret"
POST /processLogin -> 302 to /secret
GET /secret -> 200, secret resource displayed
Analysis: Same problems as above. Added problem that the URL displayed by the browser during login changes, which is confusing to the user and breaks bookmarking, link sharing, etc.
Option 3: Display login screen, display desired page
User clicks bookmark
GET /secret -> 200, surreptitiously display login form with action="/secret"
POST /secret -> 200, secret resource displayed
Analysis: Sadly, the refresh button is now also broken: refresh will cause the user agent to re-POST with a warning, instead of re-GETing /secret. They user gets a warning, but if they ignore it, something bad happens.
On the bright side, you minimize roundtrips with this technique.
Option 4: Redirect to login screen, display desired page
User clicks bookmark
GET /secret -> 302 to /processLogin
GET /processLogin via redirect -> 200, login form displayed with action="/secret"
POST /secret -> 302 to /secret
GET /secret -> 200, secret resource displayed
Analysis: Same problems as options 2+4.
Option 5: ???
Is there another technique I'm missing?
In general, which of these techniques would you recommend?
See Also
What is correct HTTP status code when redirecting to a login page?
What kind of HTTP redirect for logins?
HTTP response with redirect, but without roundtrip?
Option 1 & 3 are not following the HTTP RFC as "surreptitiously display login form" contradicts 200 GET response, where "an entity corresponding to the requested resource is sent in the response" is expected.
Option 2 is OK. All modern browsers support 302 on POST and many REST-based frameworks (like RoR) actively use it. Alternatively in "302 to /login" you can already create the session (cookie) and store the URL in session, to avoid passing the original URL in GET parameters. From usability standpoint, you can have an appropriate message on login page too (I think the URL mismatch is irrelevant here - you can't let the user see the content anyway).
Option 4: when you POST to /secret, HTTP RFC expects you to "accept the entity enclosed in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line", but all you are doing is logging in and not creating anything new under /secret.
So following HTTP RFC, your best choice is Option 2. Actually Option 2 is also in line with POST->Redirect->GET design pattern, which helps to address the issue of unpredictability in bookmarking URLs to POST'ed resources.
My $.02: I recently implemented using option 2 (although I stored /secret in a session, not in the login form as a hidden field).
I don't entirely share your concerns:
Added problem that the URL displayed
by the browser during login changes, which is confusing to the user
and breaks bookmarking, link sharing, etc.
Redirecting to /login, and the subsequent change of URL, tells the user that before they can continue there's something else that needs to be done first: logging in.
Since a login page will look entirely different from the 'target page', I don't see how that will confuse people into bookmarking and/or link sharing the login page instead of the target page (since the login page won't contain the information they want to bookmark/share anyway).
And if you're worried about 302's breaking the standard (although every single browser I know will happily break it), consider using 303's instead.
Note that mickeyreiss is correct, using AJAX Option 3 works without the drawback of the broken back button. However, it means the user has to have JavaScript enabled. This being said, if you program your form properly, you can detect whether JS is present, if not use Option 1.
Note that the 302 response is fine, however, you may have problems with caches. You have to make sure that nothing gets cached if you want to show 2 completely different pages/forms on for the same URI. (/secret showing the login and then the actual secret.)
I almost always use option #2, simply because the content returned by a URL is consistent. While today's secrets are hidden behind a login, tomorrow you may want to open it up or display mixed public/secret depending on authentication at the same URL. In that case, option #2 will be most like what Google would expect. Any content bait and switch is looked down on by Google and in the extreme case, all of your pages would have duplicate page content (ie. login form).
I would choose the option using AJAX:
login page and hide the content
the user enters the login and the password.
Authentication is done in the server side.
The server returns a result
if successful use location.href to set the page you would like to
go to, or else you can output a message saying the login is not
valid.
In your server you will be testing on a _SESSION variable, if not set redirect to the login page..
Page Load, as a sentence of 2 words, means when the page is loaded, means, when all elements are loaded.
Let's say I have a page called Ask.aspx, and this page is only allowed to users who have signed in, so technically I would write something like this :
if(Session["id"]==null)
Response.Redirect("Login.aspx");
This mean, that I'm testing the Session AFTER the page loads, theoretically, I think it sounds wrong, now of course I won't notice it, it will be fast, I will try to access the page, then I'm redirected to Login.aspx, but... is it correct to test the Session on Page Load method?
The Page_Load is part of the page lifecycle. It is called when the Server loads the page, not when the Client loads the page...
So this is the correct place to check the Session Variable...
You're actually saying: Before I post the page back to the client, check if I have the ID property set for this session... If I don't - tell the client to redirect to the Login.aspx page...
This is the correct way of doing this...
I recommened you also read about Server.Transfer. The difference between it and Response.Redirect is that in Server.Transfer the server itself "redirects" to another page and outputs the result of the new page back to the client (without the client knowing about it).
If you are trying to limit access to specific pages, you would be better off using forms authentication.
http://support.microsoft.com/kb/301240
It is fairly easy to setup and it allows checking of credentials before the request is passed to the asp.net pipeline. In what you are doing, your page goes through the entire lifecycle (controls are rendred and bound to data, access to database, calls to web services etc.) before the request is rejected. Depending on your situation, this might be costly and will not scale well.
Edit: You can also hook in to the AcquireRequestState event in the global.asax. This will also spare the entire page life cycle.
What is the difference between the following:
Server.transfer?
Response.Redirect?
Postbackurl?
When should I decide to use which?
Server.Transfer tells ASP.NET to redirect processing to another page within the same application. This happens completely server side. This is more "efficient" as it happens on the server side but there are some limitations with this method. The link below describes some of these.
Response.Redirect actually sends a HTTP 302 status code back to client in the response with a different location. The client is then responsible for following the new location. There is another round trip happening here.
PostBackUrl is not a "transfer method" but rather an property that tells the browser which URL to post the form to. By default the form will post back to itself on the server.
Here's a good link: http://haacked.com/archive/2004/10/06/responseredirectverseservertransfer.aspx
Server.Transer() works server-side. It will reply to the client with a different page than the client requested. If the client refreshes (F5), he will refresh the original page.
Response.Redirect() replies to the client that it should go to a different page. This requires an additional roundtrip, but the client will know about the redirect, so F5 will request the destination page.
PostbackUrl is a property telling an ASP control where to go when clicked on the client. This does not require an additional round trip while keeping the client informed. If you can use this method, it's generally preferable to the other choices.
Server.Transfer:
Transfers request from one page to other on server.
e.g. Browser request for /page1.aspx
Request comes on page1 where you do Server.Transfer("/page2.aspx") so request transfers to page2 And page2 returns in response but browser's address bar remains showing URL of /page1.aspx
Response.Redirect
This statements tells browser to request for next page. In this case browser's address bar also changes and shows new page URL
PostBackUrl
You can mention it on Buttons or Link buttons. This will submit the form to the page provided. It is similar to the:
<form method="post" action="/page2.aspx">
is there a way from a asp.net-page code behind with "Request.Redirect()" or another method to redirect to the last page (like Javascript history back)?
You can check the Request.UrlReferrer property, which will be set if the user has navigated to the given page from another one. This is nothing more than the HTTP Referrer header that a browser will set. This will be null if the user navigates to your page directly.
HTTP is stateless, so theres no way of being able to read the browsers history (on the server) in the same way that Javascript can (its client side).
However there are a couple of tricks you can use:
Javascript could write the URL into a textbox which gets submitted to the server
The last URL visited could be stored in session - which can be retreived on a later visit
If using the URL in session method, you'll probably want to code this into a HTTP handler (not module) and this will fire automatically on every request.
Obviously these will only work if the user has previously visited a page, and not directly.
What is difference between Server.Transfer and Response.Redirect?
What are advantages and disadvantages of each?
When is one appropriate over the other?
When is one not appropriate?
Response.Redirect simply sends a message (HTTP 302) down to the browser.
Server.Transfer happens without the browser knowing anything, the browser request a page, but the server returns the content of another.
Response.Redirect() will send you to a new page, update the address bar and add it to the Browser History. On your browser you can click back.
Server.Transfer() does not change the address bar. You cannot hit back.
I use Server.Transfer() when I don't want the user to see where I am going. Sometimes on a "loading" type page.
Otherwise I'll always use Response.Redirect().
To be Short: Response.Redirect simply tells the browser to visit another page. Server.Transfer helps reduce server requests, keeps the URL the same and, with a little bug-bashing, allows you to transfer the query string and form variables.
Something I found and agree with (source):
Server.Transfer is similar in that it sends the user to another page
with a statement such as Server.Transfer("WebForm2.aspx"). However,
the statement has a number of distinct advantages and disadvantages.
Firstly, transferring to another page using Server.Transfer
conserves server resources. Instead of telling the browser to
redirect, it simply changes the "focus" on the Web server and
transfers the request. This means you don't get quite as many HTTP
requests coming through, which therefore eases the pressure on your
Web server and makes your applications run faster.
But watch out: because the "transfer" process can work on only those
sites running on the server; you can't use Server.Transfer to send
the user to an external site. Only Response.Redirect can do that.
Secondly, Server.Transfer maintains the original URL in the browser.
This can really help streamline data entry techniques, although it may
make for confusion when debugging.
That's not all: The Server.Transfer method also has a second
parameter—"preserveForm". If you set this to True, using a statement
such as Server.Transfer("WebForm2.aspx", True), the existing query
string and any form variables will still be available to the page you
are transferring to.
For example, if your WebForm1.aspx has a TextBox control called
TextBox1 and you transferred to WebForm2.aspx with the preserveForm
parameter set to True, you'd be able to retrieve the value of the
original page TextBox control by referencing
Request.Form("TextBox1").
Response.Redirect() should be used when:
we want to redirect the request to some plain HTML pages on our server or to some other web server
we don't care about causing additional roundtrips to the server on each request
we do not need to preserve Query String and Form Variables from the original request
we want our users to be able to see the new redirected URL where he is redirected in his browser (and be able to bookmark it if its necessary)
Server.Transfer() should be used when:
we want to transfer current page request to another .aspx page on the same server
we want to preserve server resources and avoid the unnecessary roundtrips to the server
we want to preserve Query String and Form Variables (optionally)
we don't need to show the real URL where we redirected the request in the users Web Browser
Response.Redirect redirects page to another page after first page arrives to client. So client knows the redirection.
Server.Transfer quits current execution of the page. Client does not know the redirection. It allows you to transfer the query string and form variables.
So it depends to your needs to choose which is better.
"response.redirect" and "server.transfer" helps to transfer user from one page to other page while the page is executing. But the way they do this transfer / redirect is very different.
In case you are visual guy and would like see demonstration rather than theory I would suggest to see the below facebook video which explains the difference in a more demonstrative way.
https://www.facebook.com/photo.php?v=762186150488997
The main difference between them is who does the transfer. In "response.redirect" the transfer is done by the browser while in "server.transfer" it’s done by the server. Let us try to understand this statement in a more detail manner.
In "Server.Transfer" following is the sequence of how transfer happens:-
1.User sends a request to an ASP.NET page. In the below figure the request is sent to "WebForm1" and we would like to navigate to "Webform2".
2.Server starts executing "Webform1" and the life cycle of the page starts. But before the complete life cycle of the page is completed “Server.transfer” happens to "WebForm2".
3."Webform2" page object is created, full page life cycle is executed and output HTML response is then sent to the browser.
While in "Response.Redirect" following is the sequence of events for navigation:-
1.Client (browser) sends a request to a page. In the below figure the request is sent to "WebForm1" and we would like to navigate to "Webform2".
2.Life cycle of "Webform1" starts executing. But in between of the life cycle "Response.Redirect" happens.
3.Now rather than server doing a redirect , he sends a HTTP 302 command to the browser. This command tells the browser that he has to initiate a GET request to "Webform2.aspx" page.
4.Browser interprets the 302 command and sends a GET request for "Webform2.aspx".
In other words "Server.Transfer" is executed by the server while "Response.Redirect" is executed by thr browser. "Response.Redirect" needs to two requests to do a redirect of the page.
So when to use "Server.Transfer" and when to use "Response.Redirect" ?
Use "Server.Transfer" when you want to navigate pages which reside on the same server, use "Response.Redirect" when you want to navigate between pages which resides on different server and domain.
Below is a summary table of which chalks out differences and in which scenario to use.
The beauty of Server.Transfer is what you can do with it:
TextBox myTxt = (TextBox)this.Page.PreviousPage.FindControl("TextBoxID");
You can get anything from your previous page using the above method as long as you use Server.Transfer but not Response.Redirect
In addition to ScarletGarden's comment, you also need to consider the impact of search engines and your redirect. Has this page moved permanently? Temporarily? It makes a difference.
see: Response.Redirect vs. "301 Moved Permanently":
We've all used Response.Redirect at
one time or another. It's the quick
and easy way to get visitors pointed
in the right direction if they somehow
end up in the wrong place. But did you
know that Response.Redirect sends an
HTTP response status code of "302
Found" when you might really want to
send "301 Moved Permanently"?
The distinction seems small, but in
certain cases it can actually make a
big difference. For example, if you
use a "301 Moved Permanently" response
code, most search engines will remove
the outdated link from their index and
replace it with the new one. If you
use "302 Found", they'll continue
returning to the old page...
There are many differences as specified above. Apart from above all, there is one more difference. Response.Redirect() can be used to redirect user to any page which is not part of the application but Server.Transfer() can only be used to redirect user within the application.
//This will work.
Response.Redirect("http://www.google.com");
//This will not work.
Server.Transfer("http://www.google.com");
Transfer is entirely server-side. Client address bar stays constant. Some complexity about the transfer of context between requests. Flushing and restarting page handlers can be expensive so do your transfer early in the pipeline e.g. in an HttpModule during BeginRequest. Read the MSDN docs carefully, and test and understand the new values of HttpContext.Request - especially in Postback scenarios. We usually use Server.Transfer for error scenarios.
Redirect terminates the request with a 302 status and client-side roundtrip response with and internally eats an exception (minor server perf hit - depends how many you do a day) Client then navigates to new address. Browser address bar & history updates etc. Client pays the cost of an extra roundtrip - cost varies depending on latency. In our business we redirect a lot we wrote our own module to avoid the exception cost.
Response.Redirect is more costly since it adds an extra trip to the server to figure out where to go.
Server.Transfer is more efficient however it can be a little mis-leading to the user since the Url doesn't physically change.
In my experience, the difference in performance has not been significant enough to use the latter approach
Server.Transfer doesn't change the URL in the client browser, so effectively the browser does not know you changed to another server-side handler. Response.Redirect tells the browser to move to a different page, so the url in the titlebar changes.
Server.Transfer is slightly faster since it avoids one roundtrip to the server, but the non-change of url may be either good or bad for you, depending on what you're trying to do.
Response.Redirect: tells the browser that the requested page can be found at a new location. The browser then initiates another request to the new page loading its contents in the browser. This results in two requests by the browser.
Server.Transfer: It transfers execution from the first page to the second page on the server. As far as the browser client is concerned, it made one request and the initial page is the one responding with content.
The benefit of this approach is one less round trip to the server from the client browser. Also, any posted form variables and query string parameters are available to the second page as well.
Just more details about Transfer(), it's actually is Server.Execute() + Response.End(), its source code is below (from Mono/.net 4.0):
public void Transfer (string path, bool preserveForm)
{
this.Execute (path, null, preserveForm, true);
this.context.Response.End ();
}
and for Execute(), what it is to run is the handler of the given path, see
ASP.NET does not verify that the current user is authorized to view the resource delivered by the Execute method. Although the ASP.NET authorization and authentication logic runs before the original resource handler is called, ASP.NET directly calls the handler indicated by the Execute method and does not rerun authentication and authorization logic for the new resource. If your application's security policy requires clients to have appropriate authorization to access the resource, the application should force reauthorization or provide a custom access-control mechanism.
You can force reauthorization by using the Redirect method instead of the Execute method. Redirect performs a client-side redirect in which the browser requests the new resource. Because this redirect is a new request entering the system, it is subjected to all the authentication and authorization logic of both Internet Information Services (IIS) and ASP.NET security policy.
-from MSDN
Response.Redirect involves an extra round trip and updates the address bar.
Server.Transfer does not cause the address bar to change, the server responds to the request with content from another page
e.g.
Response.Redirect:-
On the client the browser requests a page http://InitiallyRequestedPage.aspx
On the server responds to the request with 302 passing the redirect address http://AnotherPage.aspx.
On the client the browser makes a second request to the address http://AnotherPage.aspx.
On the server responds with content from http://AnotherPage.aspx
Server.Transfer:-
On the client browser requests a page http://InitiallyRequestedPage.aspx
On the server Server.Transfer to http://AnotherPage.aspx
On the server the response is made to the request for http://InitiallyRequestedPage.aspx passing back content from http://AnotherPage.aspx
Response.Redirect
Pros:-
RESTful - It changes the address bar, the address can be used to record changes of state inbetween requests.
Cons:-
Slow - There is an extra round-trip between the client and server. This can be expensive when there is substantial latency between the client and the server.
Server.Transfer
Pros:-
Quick.
Cons:-
State lost - If you're using Server.Transfer to change the state of the application in response to post backs, if the page is then reloaded that state will be lost, as the address bar will be the same as it was on the first request.
Response.Redirect
Response.Redirect() will send you to a new page, update the address bar and add it to the Browser History. On your browser you can click back.
It redirects the request to some plain HTML pages on our server or to some other web server.
It causes additional roundtrips to the server on each request.
It doesn’t preserve Query String and Form Variables from the original request.
It enables to see the new redirected URL where it is redirected in the browser (and be able to bookmark it if it’s necessary).
Response. Redirect simply sends a message down to the (HTTP 302) browser.
Server.Transfer
Server.Transfer() does not change the address bar, we cannot hit back.One should use Server.Transfer() when he/she doesn’t want the user to see where he is going. Sometime on a "loading" type page.
It transfers current page request to another .aspx page on the same server.
It preserves server resources and avoids the unnecessary roundtrips to the server.
It preserves Query String and Form Variables (optionally).
It doesn’t show the real URL where it redirects the request in the users Web Browser.
Server.Transfer happens without the browser knowing anything, the browser request a page, but the server returns the content of another.