Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Loading an ASP.NET Page Class dynamically in an HttpHandler


:P
On this page:

I ran into an interesting post on the ASP.NET forums today where someone is trying to dynamically invoke a page class inside of an HttpHandler. Basically the idea is that you have a class for the page that exist and you're dynamically invoking the class rather than letting the ASP.NET BuildManager deal with loading the page from disk and doing its thing.

This can be pretty useful in a few scenarios - for example you can potentially use library projects to pre-compile ASP.NET pages completely and then execute them from another assembly with everything self contained in the external assembly.

Anyway the basic loading process is pretty simple - you  create a handler and once you're sure you have a compiled class you just instantiate the class and execute it. Remember that the ASP.NET Page class is just an HttpHandler so you can do something like this:

    public class TestHandler : System.Web.IHttpHandler
    {

        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(System.Web.HttpContext context)
        {

            //Page page = BuildManager.CreateInstanceFromVirtualPath("/weblog/schedulerTest.aspx", typeof(SchedulerTest) ) as Page; 
            //BuildManager.GetVirtualPathDependencies("/weblog/testpage.axd");

            Page page = new SchedulerTest();  // An ASPX Page class (note: CodeBehind)
            page.AppRelativeVirtualPath = context.Request.AppRelativeCurrentExecutionFilePath; 
            page.ProcessRequest(context);
            
        }
    }

There are a couple of big gotchas with this though.

First notice the AppRelativePath assignment  which is crucial. Normally the BuildManager and the Page factory handle instantiation of the page from disk. The BuildManager handles a number of important tasks like making sure the page is fully compiled and deciding which class actually gets instantiated and executed. If you don't set the AppRelativeVirtualPath any ResolveUrl() calls will fail to resolve.

The second issue is that code like the above depends on pages being pre-compiled and that you instantiate the right class. For example, if you're using Stock projects the name of page class by default is dynamically generated (you can override it by specifying the ClassName attribute).  In Web Application Projects this is even more complicated because the ASPX page is completely dynamically generated at runtime so you can't actually reference the ASPX page directly (the IDE doesn't precompile it for you like stock projects do). So the code above referencing SchedulerTest is actually referencing the CodeBehind class (in WAP) NOT the actual ASPX page which means you don't get markup rendering. This means executing this page only executes the CodeBehind, and not any of the page markup and controls.

This may still be useful if you are using Page subclasses for dynamically generating content. For example, if you use a custom framework to dynamically add controls to a page in CodeBehind or a custom parser etc. etc. this approach works just fine.

But if you need the ASPX markup to render as well things are much more complicated because ASP.NET generates the ASPX via precompilation - ie. dynamically. So that makes it much harder to reference the generated type dynamically.

The bottom line is this: Trying to execute a page dynamically entirely is best done by using the virtual path to launch it. Rather than instantiating the class use a the BuildManager.CreateInstance() to point at the virtual path or rewrite the path to the ASPX file.

Packaging: Use Web Deployment Projects

If you really need to package pages into a fully self contained assembly and execute them from there, you have to use Web Deployment projects to generate the  standalone compiled assembly. Make sure that you use ClassName="" on each page to give the name a concrete name you can reference. Once you have the compiled with WDP you can add areference the generated assembly to your project and then reference the class from there. That will let you instantiate the ASPX class which inherits from the CodeBehind/CodeBeside. The AppRelativeVirtualPath still needs to be set in this scenario as well.

This is a very special case scenario, but I've had a few occasions when this has come in handy. I have one application that has an administration ASPX interface which is basically shipped as a self contained assembly. It works without having to copy files to disk first. It's a bit of effort to make this work, but if you need it it's a life saver.

Note though that I found it's still difficult to get everything wrapped into a single assembly. WDP still generates a few files especially if you have localized resources associated with the page. So if your goal is packaging of components I've found that it may not always be worth the effort and instead just ship a directory that can be installed with a given site. <shrug>

Posted in ASP.NET  

The Voices of Reason


 

Anatoly
July 23, 2007

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Very nice.
Always want to do that but nobody knows how.

P.S. Can I register HttpHandler dynamicly (by code) or I have to do that on web.config?

Thanks

Rick Strahl
July 23, 2007

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

HttpHandlers must be added in web.config as far as I know. The problem is that ASP.NET stores handlers to be loaded in a read-only collection that can't be added to. HttpModules can be registered with some trickery (http://west-wind.com/WebLog/posts/44979.aspx) and it's possible to essentially fake handler operation through a module by executing code in one of the pre handler events like PostResolveRequestCache().

Anatoly
July 23, 2007

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Thanks, Rick

Sebastian Patten
July 16, 2008

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Thanks, I've ran into your posts quite frequently recently and have found them all very useful. Keep up the good work!

phanf
January 24, 2009

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Thanks for the great example. I was wondering if this can be taken one step further by loading the entire web page within a windows form application where no IIS webserver is involved. Process the request and load it inside the webbrowser control.

Asame Obiomah
January 31, 2009

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Thanks Rick. I tried to do this about a year ago over several days and even more failures...

@Anatoly
You can execute a handler without registering it as below:

public class SomeHandler : IHttpHandler
{
... e.t.c.
}

HttpContext context = HttpContext.Current; //or any other useable httpcontext
IHttpHandler handler = new SomeHandler();
handler.ProcessRequest(context);

Hex
May 22, 2009

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

great!!!!!!!!!!!!
Thanks Rick.

Brijesh Gadhiya
June 23, 2009

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

One can try the other option starting .NET2.0 onwards

public override void ProcessRequest(HttpContext context)
{
Page page = (Page)System.Web.UI.Compilation.BuildManager.CreateInstanceFromVirtualPath(~/View/Pages/login.aspx);
page.ProcessRequest(context);
}

sudhi
November 24, 2010

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Hi Rick. Thats a good Article.
I need your suggestion in the following scnerio.

1.I developed a webApp in C#.
2.In which I have a aspx Page.
3.In which there is a iframe.
4.At runTime i m loading a thirdParty WebApp into iframe.
5.When i click one button in app loaded in Iframe, a request is made to a httphandler in my WebApp.
6. Now, at that situation, i want to access that aspx page and disable some controls from httphandler.
Is that possible? If possible what will be the solution.

Thanks in advance

Peter Sulek
March 14, 2012

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Check my project on codeplex - http://deployweblib.codeplex.com/ it handles exactly you want.

Oliver
May 29, 2014

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

This question in the first comment caught my attention:

"Can I register HttpHandler dynamicly (by code) or I have to do that on web.config?"

There seems to be a way to achieve this (now? it's 2014!) from inside an HttpModule (from http://stackoverflow.com/a/22980416/177710):

public class MyHttpModule : IHttpModule
{
    public void Dispose() { }
 
    public void Init(HttpApplication application)
    {
        application.PostAuthenticateRequest += application_PostAuthenticateRequest;
    }
 
    void application_PostAuthenticateRequest(object sender, EventArgs e)
    {
        var app = sender as HttpApplication;
        var requestUrl = context.Request.Url.AbsolutePath;
 
        if (requestUrl "meets criteria")
        {
            app.Context.RemapHandler(new MyHttpHandler());
        }
    }
}


Worked great for me to get around using a page in some asynchronous scenarios!

Rick Strahl
May 30, 2014

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

@Oliver - yes, you can also effectively remap handlers with Routing, which allow you to fire off handler factories. My guess is that RemapHandler() is used and probably was created for that.

Still that's something you can do only on a per request basis, not globally - so it would require a module to look at every request to re-route the request handler. The original article's point was at dynamically adding a handler into the pipeline so it fired in the same way you can dynamically attach a module in your Application_Start() handler.

varsha
August 06, 2019

# re: Loading an ASP.NET Page Class dynamically in an HttpHandler

Still that's something you can do only on a per request basis, not globally - so it would require a module to look at every request to re-route the request handler. The original article's point was at dynamically adding a handler into the pipeline so it fired in the same way you can dynamically attach a module in your Application_Start() handler.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024