Response.Redirect into a new window (with Extension Methods)

This question comes up from time to time, to time. If you understand how redirects work, then you also know it is "not possible" to redirect into a new window, because a redirect on the server causes a special HTTP response to be sent to the users browser, the client. The browsers native implementation interprets the special response code and sends the user off to the destination. There's no built-in mechanism or standard for specifying a new window.

The only way to open a new window is for it to be initiated on the client side, whether it be through script or clicking on a link.

So the solution always proposed to this problem is to instead write out some script that opens the window, rather than using Response.Redirect:

<script type="text/javascript">
    window.open("foo.aspx");
</script>

Ok... so first you lecture me about how it is "not possible", and then you give me the code that makes it possible. Why can't I just redirect to a new window -- I don't care how HTTP works or client this or server that. There's obviously a solution, so why do I have to worry about it?

(The make-believe developers in my head are always quite temperamental)

It's easy enough to write a little helper that abstracts the details away from us... while we're at it, we might as well add 'target' and 'windowFeatures' parameters. If we're going to open the new window with script, why not let you use all of the window.open parameters? For example, with 'windowFeatures' you can specify whether the new window should have a menu bar, and what its width and height are.

public static class ResponseHelper {
    public static void Redirect(string url, string target, string windowFeatures) {
        HttpContext context = HttpContext.Current;
 
        if ((String.IsNullOrEmpty(target) ||
            target.Equals("_self", StringComparison.OrdinalIgnoreCase)) &&
            String.IsNullOrEmpty(windowFeatures)) {
 
            context.Response.Redirect(url);
        }
        else {
            Page page = (Page)context.Handler;
            if (page == null) {
                throw new InvalidOperationException(
                    "Cannot redirect to new window outside Page context.");
            }
            url = page.ResolveClientUrl(url);
 
            string script;
            if (!String.IsNullOrEmpty(windowFeatures)) {
                script = @"window.open(""{0}"", ""{1}"", ""{2}"");";
            }
            else {
                script = @"window.open(""{0}"", ""{1}"");";
            }
 
            script = String.Format(script, url, target, windowFeatures);
            ScriptManager.RegisterStartupScript(page,
                typeof(Page),
                "Redirect",
                script,
                true);
        }
    }
}

Now you just call ResponseHelper.Redirect, and it figures out how to honor your wishes. If you don't specify a target or you specify the target to be "_self", then you must mean to redirect within the current window, so a regular Response.Redirect occurs. If you specify a different target, like "_blank", or if you specify window features, then you want to redirect to a new window, and we write out the appropriate script.

One nice side effect of this "do you really need a new window?" detection is that it's dynamic. Say the destination you redirect to is configurable by some administrator. Now they can decide whether it opens in a new window or not. If they don't want it to they can specify blank or _self as the target.

Disclaimers:

Note: If you use it outside the context of a Page request, you can't redirect to a new window. The reason is the need to call the ResolveClientUrl method on Page, which I can't do if there is no Page. I could have just built my own version of that method, but it's more involved than you might think to do it right. So if you need to use this from an HttpHandler other than a Page, you are on your own.

Note: Beware of popup blockers.

Note: Obviously when you are redirecting to a new window, the current window will still be hanging around. Normally redirects abort the current request -- no further processing occurs. But for these redirects, processing continues, since we still have to serve the response for the current window (which also happens to contain the script to open the new window, so it is important that it completes).

Extension Methods

Recently, Eilon and Bertrand blogged about a novel use of some C# 3.0 features. Eilon posed the question, "Have you come up with a novel way to use a new language feature that you'd like to share?". Well here you go.

Extension Methods are a new feature in C# 3.0 (you'll need it for the rest of the article). They allow you to add methods to existing types, imported via a 'using' statement. I've seen a lot of debate over their use -- whether they are bad or good. Well -- I don't know, I don't really want to be involved in that debate. But I do know that in some scenarios they seem to fit perfectly. Like all language tools, you should use it sparingly and only when appropriate. I believe even the dreaded GOTO statement, which yes, exists in C#, has its place (I wasn't a believer originally, but some old coworkers of mine convinced me (Bob!)).

In this case, an extension method seems to work well. In general, whenever you find yourself writing a static Helper class whose only purpose in life is to help use the APIs of another type, it's probably a great candidate for extension methods. Especially if the first parameter to all those methods is the type you're trying to help with -- or if the methods always grabs the instance through some static API (like HttpContext.Current) or instantiates a new one.

By rewriting our ResponseHelper to use extension methods...

public static class ResponseHelper {
    public static void Redirect(this HttpResponse response,
        string url,
        string target,
        string windowFeatures) {
 
        if ((String.IsNullOrEmpty(target) ||
            target.Equals("_self", StringComparison.OrdinalIgnoreCase)) &&
            String.IsNullOrEmpty(windowFeatures)) {
 
            response.Redirect(url);
        }
        else {
            Page page = (Page)HttpContext.Current.Handler;
            if (page == null) {
                throw new InvalidOperationException(
                    "Cannot redirect to new window outside Page context.");
            }
            url = page.ResolveClientUrl(url);
 
            string script;
            if (!String.IsNullOrEmpty(windowFeatures)) {
                script = @"window.open(""{0}"", ""{1}"", ""{2}"");";
            }
            else {
                script = @"window.open(""{0}"", ""{1}"");";
            }
 
            script = String.Format(script, url, target, windowFeatures);
            ScriptManager.RegisterStartupScript(page,
                typeof(Page),
                "Redirect",
                script,
                true);
        }
    }
}

Note the 'this' keyword in the first parameter. Now whenever we include the namespace this class is defined within, we get a nice override on the actual Response object.

ResponseRedirect

Simply including a 'using' to a namespace is what gets extensions methods to show up. So it's probably a good idea to keep extension methods isolated to their own namespaces, lest someone get more than they bargained for when they use your namespace.

Also worth noting is that this is still a static API, so you can use it the traditional way, too. You just have to pass in the Response object as the first parameter.

And to see it in action...

Response.Redirect("popup.aspx", "_blank", "menubar=0,width=100,height=100");

Redirected into a new Window...

72 Comments

  • Overloading Response.Redirect to display a popup is wrong on so many levels that it is not even funny.

    If you want to make an API to display a popup from server-side code, fine, but don't pollute the API with your code by overloading something that is intended to be an encapsulation of an HTTP response, not a JavaScript code injector.

  • Joe -- fair enough. From pure the perspective of what HttpResponse encapsulates, it is out of place. But from the perspective of a page developer, Response by itself doesn't always give you everything you want. Don't like polluting it.. then don't include the namespace, or simply remove 'this' from the parameter list, and its no longer an extention. Actually, perhaps this would be a better extention to the Page class instead of HttpResponse. That would more naturally imply the requirement for a Page instance. Yeah, makes more sense that way :)

  • Can someone convert this to VB.NET?

  • >url = page.ResolveClientUrl(url);

    Dave,

    Why are you calling this method? Some kind of security check?

    Thanks,
    Andy

  • Cool idea, I think it is a good example of extension methods! However, it may be a little bit confusing to read a code with a lot of extension methods for the framework.

  • hey Derek here you go for VB version:
    Listing 1:

    Public Class ResponseHelper
    Private Sub New()
    End Sub
    Public Shared Sub Redirect(ByVal url As String, ByVal target As String, ByVal windowFeatures As String)
    Dim context As HttpContext = HttpContext.Current
    If (String.IsNullOrEmpty(target) OrElse target.Equals("_self", StringComparison.OrdinalIgnoreCase)) AndAlso String.IsNullOrEmpty(windowFeatures) Then
    context.Response.Redirect(url)
    Else
    Dim page As Page = CType(context.Handler, Page)
    If page Is Nothing Then
    Throw New InvalidOperationException("Cannot redirect to new window outside Page context.")
    End If
    url = page.ResolveClientUrl(url)
    Dim script As String
    If (Not String.IsNullOrEmpty(windowFeatures)) Then
    script = "window.open(""{0}"", ""{1}"", ""{2}"");"
    Else
    script = "window.open(""{0}"", ""{1}"");"
    End If
    script = String.Format(script, url, target, windowFeatures)
    ScriptManager.RegisterStartupScript(page, GetType(Page), "Redirect", script, True)
    End If
    End Sub
    End Class

    Listing 2:
    Public Class ResponseHelper
    Private Sub New()
    End Sub
    _
    Public Shared Sub Redirect(ByVal response As HttpResponse, ByVal url As String, ByVal target As String, ByVal windowFeatures As String)
    If (String.IsNullOrEmpty(target) OrElse target.Equals("_self", StringComparison.OrdinalIgnoreCase)) AndAlso String.IsNullOrEmpty(windowFeatures) Then
    response.Redirect(url)
    Else
    Dim page As Page = CType(HttpContext.Current.Handler, Page)
    If page Is Nothing Then
    Throw New InvalidOperationException("Cannot redirect to new window outside Page context.")
    End If
    url = page.ResolveClientUrl(url)
    Dim script As String
    If (Not String.IsNullOrEmpty(windowFeatures)) Then
    script = "window.open(""{0}"", ""{1}"", ""{2}"");"
    Else
    script = "window.open(""{0}"", ""{1}"");"
    End If
    script = String.Format(script, url, target, windowFeatures)
    ScriptManager.RegisterStartupScript(page, GetType(Page), "Redirect", script, True)
    End If
    End Sub
    End Class

  • Andy -- not a security check. It's main purpose is to resolve the url to one usable on the client. For example, "~/foo" may resolve to "/app/foo" if app is the root of the application.

  • Actually you shouldn't really be calling Page.ResolveClientUrl - instead you should take in a reference/context control and use that to call ResolveClientUrl. I know... it complicates the signature...
    The reason being - if I am calling this method from within a user control or a master page, then the resolution needs to happen with that context, rather than the page using them.
    Personally I think this method belongs on ClientScriptManager, and not on HttpResponse.

  • so cool ,it is so easy to use according to your idea!

  • It is not big deal; I have been using same way since last 4 years!

  • Nikhil -- I thought of that, but Response.Redirect doesn't resolve based on the current template control (unless it does something crazy I'm not aware of) so why should this?

    I do think putting it on page would make more sense, which is almost the same as putting it on ClientScriptManager.

  • Response.Redirect maps almost 1:1 with a 302 status code and is a pipeline event, which is independent of page vs. user control events. This API seems more about the page framework, and hence should account for resolving against the correct context... just an opinion.

  • hurray ! its really great.

  • ROFL

    I cant help but laugh my ass off at Siraj Gadhia's comment

  • /nod

  • Nothing wrong with judicious use of GOTO's although that argument was made back when we were developing in VB6 and you didn't have structured exception handling. I have never used a GOTO in C# although I wouldn't have any problem using one if the situation called for it.

    Thanks for the reference!

  • Bob! You do read my blog. I'm honored. Now put it on the required company reading list :)

  • Hi,
    This works ok on my local development machine but not on my shared hosted environment.
    This line fails in the helper class:
    ScriptManager.RegisterStartupScript(page, GetType(Page), "Redirect", script, True)

    With the following error:
    Compilation Error: BC30451: Name 'ScriptManager' is not declared.

    Is this because I need AJAX for this to work. My hoster doesn't allow AJAX?

  • JS -- if you want it to work whether AJAX is available or not, this article is for you:

    http://weblogs.asp.net/leftslipper/archive/2006/11/13/HOWTO_3A00_-Write-controls-compatible-with-UpdatePanel-without-linking-to-the-ASP.NET-AJAX-DLL.aspx

  • Hi,

    I'm a bit confused. Do I need Visual Studio 2008 to be able to use the 'this' keyword?

    Paul.

  • PaulSinnema -- it's a C# 3.0 compiler feature. The short answer is yes you need 2008. But technically I dont think anything could stop you from using VS2005 to use C# 3.0, and it would build successfully even if VS didn't like the syntax, but I don't have any specific instructions on how to do that.

  • Thanks for this nice code!

  • hi this code works but may i know how to pass the value from the popup window to it parent widow?

  • This is an imaginative solution elegantly coded. The argument regarding override of Response is two edged. Perhaps it should be on Page, but we override methods every day if it gets the job done. Injecting script through this method won't please everyone. Personally, I like it. I think the real problem is the schism between client and server (AJAX is kind of an attempt to address this) that prevents the Response Class from implementing this function itself, (maybe as an extension of or parameter to, Response.Redirect). Thanks for posting it.

  • thanks very useful!

    I used current time as the name of the frame in order to load multiple unique tabs.

    ResponseHelper.Redirect(vquery, DateTime.Now.Second.ToString(), "");

  • bpd -- if you use _blank it will always open a new one, no need to try and make every name unique.

  • Absolutely ingenious! Thank you!

  • It's a nice work.

    I've a problem. The code is running correctly in my local, but it is not running in my server (the page is hosted in manashosting.com).No error message is didplaying,

    In button event i've given 2 function.
    1. is to show a image
    2. to redirect to another page.

    the image is showing correctly but the new page is not opening.

    And u've given
    Response.Redirect("popup.aspx", "_blank", "menubar=0,width=100,height=100");

    but for me it is working as like this

    ResponseHelper.Redirect "Authenticated.aspx", "_blank", "width=550px; heigth=800px;");

    pls give a correct and fine reply.

  • 'ScriptManager' does not exist in the current context
    Please tell me what is the issue with this.

    Thanx

  • This is EXACTLY what I needed. Thanks so much!!!!!!!!

  • Brilliant! Works like a charm. Cheers

  • This is super useful!!!
    thank you so much!

  • OMG I never thought it is really possible to open new window on server side like that. You are a genius :O.


    Thanks heaps

  • Is there a way to redirect to a new window, and then have the parent window redirect to another window?

    Example: I have a page on which the user clicks a button that will open a new window. Upon that happening, I want the page on which the user clicked the button to goto another page.

    Currently, if I use the ResponseHelper, followed by a Response.Redirect, the Response.Redirect is never invoked.

    Thanks.

  • vbjox -- you will have to emit some script which sets window.location to the new page you want the main window to go to.

  • Thanks so much. Saved me a lot of heartache :)

  • Thank you very very very much

  • Thank you for the codes. I put the codes in an imagebutton click event. The codes enable me to click an imagebutton in a gridview to open a new page with enlarged image displayed. But the problem is the new page hides behind its parents' page. This is probably because after the new page loads, the parents' page runs its page load again. This leads the new page losts focus. Can someone help me with the issue? Thanks.

  • thanks for billions times.

  • This was a very elegant solution. I carried over varibles to the pop up via session varibles. I am not sure that is the best way but it seems to work easy enough.

  • great, finally i can call pop up windows from a pop up. thanks

  • First i want to congratulate you for your brilliant solution. Is there any way to get past poup blockers?

    Thanks again.

  • I like it and I'm using for my project. But Micro$oft doesn't seem to like extension methods very much. From the M$ documentation:

    In general, we recommend that you implement extension methods sparingly and only when you have to. Whenever possible, client code that must extend an existing type should do so by creating a new type derived from the existing type. For more information, see Inheritance (C# Programming Guide).

    When using an extension method to extend a type whose source code you cannot change, you run the risk that a change in the implementation of the type will cause your extension method to break.

    Bunch of whiners.

  • Mike, you're kidding right? :) They add a feature and then give guidance on how best to use it and you think it's whining? Wow.

  • Your explanation and the code are very good, but please change the background color, is absolutely unreadable. Why not use the humble and traditional white background or another soft color?

    Thanks

  • Let me also add my thanks for this excellent piece of code. It's probably the only way to pop a page from the "Select" button of a GridView. I used it and it works fine. Now, if I only could get around the problem of the new page hiding behind the old one ...

  • This is a great idea and it almost got me where I wanted to go...

    I use your piece of code to open multiple windows, but I want to use query string parameters in my urls.

    However, as soon as I add "?Parameter=value", the new windows all display "404 not found" with the correct url in the address bar. I press enter in the new windows and they display the page I wanted.

    Do you know why?

  • Solution to the problem of the newly created page hiding behind its parent:
    Add the following in the child page:

  • Thanks for the solution.

    Regards.

  • This is great! I too used it to pop a page from a grid, and it works great. And it is the appropriate use of an Extension method in that the HttpResponse class is sealed.

  • I am not able to implement it in my page.
    please help me how can i use this code?
    should i write the above given code in new .cs file or in the same aspx.cs file?
    how can i overload the response.redirect method?

  • If it works, it works. I don't care about standards and all that hoopala. Thanks for making it simple. :-)

  • I've got a problem with this code using IE (currently version 8 maybe it occurs in previous version also), the newly created window/tab still don't get any focus. It works fine on other browsers.
    After trying "onload="javascript:self.focus()" on new window it still don't work. Is there any solution for this ? Thanks in advance.

  • Thanks. I put it good use immediately!

  • Hello from Russia!
    Can I quote a post in your blog with the link to you?

  • Polprav -- go for it :)

  • Can I use your (extension) method also to open up e.g. an Acrobat reader file? If so any help in the (preferably VB) code to make this happen would be much appreciated? (The file is coming from a database and contained in a Byte variable.)

  • Hi Everyone
    when i implement the above functionality i get the following error

    "object reference not set to an instance of an object"

    can any one help me to implement it step by step


  • excellent solution for a very common and frustraiting issue many developers come across.

  • düğün davetiyesi ve davetiye sözleri

  • good code but of no help because in most of the users keep pop up blocker on

  • Great....Just What I needed!!!! :) Thanks so much for posting!

  • Hi,

    The code worked. However, I have problem where I am redirecting to a URL of a ".CSV" file (e.g.: http://mywebsite/work/report.csv), and when calling this new Helper Redirect function, the new window is being opened perfectly and download window appears, but for some reason, the file is being converted to ".XLS" format (Excel format).

    Any idea what could be the reason and how to fix it?

  • @salan_alani: This code has nothing to do with that. If it's being converted, it's because your computer has Excel associated with the CSV file extension. The browser is merely trying to open the appropriate application for the file type. If you want a different app to open, you should set the appropriate content type for that file.

  • Good piece of code. Used this to intitate a pdf download from a handler after setting some sessions on postback in the web page and using the above code to call the handler for binarywrite the pdf byte array.

    One issue though with IE8 , IE 8 blocks the pdf content emitted bythe handler file withe message "To help protect your security, Internet Explorer blocked this site from downlaoding files to your computer."

    Is there any way around this?

  • This is write code for visual basic if we want to override method on the actual Response object:

    Imports Microsoft.VisualBasic
    Imports System.Runtime.CompilerServices

    Public Module ResponseExtension
    _
    Public Sub Redirect(ByVal aHttpResponse As HttpResponse, ByVal url As String, ByVal target As String, ByVal windowFeatures As String)

    If (String.IsNullOrEmpty(target) OrElse target.Equals("_self", StringComparison.OrdinalIgnoreCase)) AndAlso String.IsNullOrEmpty(windowFeatures) Then

    aHttpResponse.Redirect(url)

    Else

    Dim page As Page = CType(HttpContext.Current.Handler, Page)

    If page Is Nothing Then

    Throw New InvalidOperationException("Cannot redirect to new window outside Page context.")

    End If

    url = page.ResolveClientUrl(url)

    Dim script As String

    If (Not String.IsNullOrEmpty(windowFeatures)) Then

    script = "window.open(""{0}"", ""{1}"", ""{2}"");"

    Else

    script = "window.open(""{0}"", ""{1}"");"

    End If

    script = String.Format(script, url, target, windowFeatures)

    ScriptManager.RegisterStartupScript(page, GetType(Page), "Redirect", script, True)

    End If
    End Sub
    End Module

  • but where i write That Static class.


    send me o my email ID

  • It's work. Thank you for good idea.
    And thanks also to vb version.

  • I was looking for this one Exactly.
    very good use of Extension method..

  • I've used this code in many websites I've created, THANKX.

    I just tried it in an old 2.0 website and it didn't work, it seems that it will only work in 3.5 and ahead!

  • I love it when people have already done the work for me. Good show.

  • Nothing but to thank you man. Hell of an extension method :) Again, thank you very much.

Comments have been disabled for this content.