I need to do routing in an existing asp.net app - not an asp.net mvc (yeah I know I should convert but let's say it's not possible right now so don't tell me :) ) - how can I do routing to a normal class instead of an aspx page as all sample code I see is always with aspx page like here:
http://msdn.microsoft.com/en-us/magazine/dd347546.aspx
To precise, I want to do a bit like in MVC Controller routing : the controller for example product is a pure class you access through http://domain.com/product
ASP.NET MVC and ASP.NET Web Forms share the same routing infrastructure in that both frameworks ultimately need to come up with an IHttpHandler to handle the HTTP request:
The IHttpHandler interface has been a part of ASP.NET since the
beginning, and a Web Form (a System.Web.UI.Page) is an IHttpHandler.
(From the MSDN article linked in the question)
In ASP.NET MVC the System.Web.Mvc.MvcHandler class is used, which then delegates to a controller for further handling of the request. In ASP.NET Web Forms usually the System.Web.UI.Page class that represents an .aspx file is used, but a pure IHttpHandler associated with .ashx file can also be used.
So you can route to an .ashx handler as an alternative to an .aspx Web Forms page. Both implement IHttpHandler (as does MvcHandler), but with the former that's all it does. And that's as close as you can get to a 'pure class' handling a (routed) request. And since the handler part is just an interface, you are free to inherit from your own class.
<%# WebHandler Language="C#" Class="LightweightHandler" %>
using System.Web;
public class LightweightHandler : YourBaseClass, IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Hello world!");
}
public bool IsReusable { get { return false; } }
}
Notice that an IRouteHandler just needs to return an instance of IHttpHandler:
public IHttpHandler GetHttpHandler(RequestContext requestContext);
You may need to jump through some hoops to instantiate your handler using the BuildManager* if you use .ashx files. If not, you can just new up an instance of your class and return it:
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// In case of an .ashx file, otherwise just new up an instance of a class here
IHttpHandler handler =
BuildManager.CreateInstanceFromVirtualPath(path, typeof(IHttpHandler)) as IHttpHandler;
// Cast to your base class in order to make it work for you
YourBaseClass instance = handler as YourBaseClass;
instance.Setting = 42;
instance.DoWork();
// But return it as an IHttpHandler still, as it needs to do ProcessRequest
return handler;
}
See the answers to this question for much more in-depth analysis of routing pure IHttpHandlers: Can ASP.NET Routing be used to create “clean” URLs for .ashx (IHttpHander) handlers?
**I'm not entirely sure about the BuildManager example, someone please correct me if I got that part wrong*
If you can't switch to ASP.NET MVC and routing .ashx handlers doesn't meet your requirements, you may want to look into Nancy, a 'lightweight web framework'.
Here's an example from the introduction (see link in previous paragraph):
public class Module : NancyModule
{
public Module() : base("/foo")
{
Get["/"] = parameters => {
return "This is the site route";
};
Delete["/product/{id}"] = parameters => {
return string.Concat("You requested that the following product should be deleted: ", parameters.id);
};
}
}
This class will handle requests to /foo and /foo/product/42. You can also use views with this framework to render a more complex (HTML) response.
If you can update from 3.5 to 4.0, WebForms supports routing better. In Global.asax, you only need to do things like this:
void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.MapPageRoute("default", string.Empty, "~/default.aspx");
}
I don't really understand the "pure class" part, but hopefully if updating to 4.0 is an option this can get you going.
Related
I have a .Net MVC web application (Not WebAPI), and I want to intercept all calls to the web app before they reach the controller, check for a value in the request headers, and do something if the value isn't present (such as presenting a 404). What's the ideal way to do this? Keep in mind this is not a Web API application, just a simple web application.
Depending on what specifically you want to do, you could use a default controller which all other controllers extend. That way you can override OnActionExecuting or Initialize and do your check there.
public class ApplicationController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//do your stuff here
}
}
public class YourController : ApplicationController
{
}
You're looking for global action filters.
Create a class that inherits ActionFilterAttribute, override OnActionExecuting() to perform your processing, and add an instances to global filter collection in Global.asax.cs (inside RegisterGlobalFilters())
I was looking for a way to route http://www.example.com/WebService.asmx to http://www.example.com/service/ using only the ASP.NET 3.5 Routing framework without needing to configure the IIS server.
Until now I have done what most tutorials told me, added a reference to the routing assembly, configured stuff in the web.config, added this to the Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
RouteCollection routes = RouteTable.Routes;
routes.Add(
"WebService",
new Route("service/{*Action}", new WebServiceRouteHandler())
);
}
...created this class:
public class WebServiceRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// What now?
}
}
...and the problem is right there, I don't know what to do. The tutorials and guides I've read use routing for pages, not webservices. Is this even possible?
Ps: The route handler is working, I can visit /service/ and it throws the NotImplementedException I left in the GetHttpHandler method.
Just thought I would round off this question with a bit more of a detailed solution based on the answer given by Markives that worked for me.
Firstly here is the route handler class which takes the virtual directory to your WebService as its constructor param.
public class WebServiceRouteHandler : IRouteHandler
{
private string _VirtualPath;
public WebServiceRouteHandler(string virtualPath)
{
_VirtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new WebServiceHandlerFactory().GetHandler(HttpContext.Current,
"*",
_VirtualPath,
HttpContext.Current.Server.MapPath(_VirtualPath));
}
}
and the actual usage of this class within the routey bit of Global.asax
routes.Add("SOAP",
new Route("soap", new WebServiceRouteHandler("~/Services/SoapQuery.asmx")));
This is for anyone else who wants to do the above. I found it incredibly difficult to find the information.
In the GetHttpHandler(byVal requestContext as RequestContext) as IHttpHandler Implements IRouteHandler.GetHttpHandler method (my version of the above)
This is for Webforms 3.5 by the way (mine's in VB).
You cant use the usual BuildManager.CreateInstanceFromVirtualPath() method to invoke your web servce that's only for things that implement iHttpHandler which .asmx doesn't. Instead you need to:
Return New WebServiceHandlerFactory().GetHandler(
HttpContext.Current, "*", "/VirtualPathTo/myWebService.asmx",
HttpContext.Current.Server.MapPath("/VirtualPathTo/MyWebService.aspx"))
The MSDN documentation says that the 3rd parameter should be the RawURL, passing HttpContext.Current.Request.RawURL doesn't work, but passing the virtual path to the .asmx file instead works great.
I use this functionality so that my webservice can be called by any Website configured in anyway (even a virtual directory) that points (in IIS) to my application can call the application web service using something like "http://url/virtualdirectory/anythingelse/WebService" and the routing will always route this to my .asmx file.
You need to return an object that implements IHttpHandler, that takes care of your request.
You can check out this article on how to implement a webservice using that interface: http://mikehadlow.blogspot.com/2007/03/writing-raw-web-service-using.html
But this is probably closer to what you want http://forums.asp.net/p/1013552/1357951.aspx (There is a link, but it requires registration, so I didnt test)
I am building a web site with ASP.NET 3.5, and most of the site structure is static enough to create a folder structure and aspx pages. However, the site administrators want the ability to add new pages to different sections of the site through a web interface and using a WYSIWYG editor. I am using nested master pages to give the different sections of the site their own menus. What I would like to do is have a generic page under each section of the site that uses the appropriate master page and has a place holder for content that could be loaded from a database. I would also like these "fake" pages to have a url like any other aspx page, as if they had corresponding files on the server. So rather than have my url be:
http://mysite.com/subsection/gerenicconent.aspx?contentid=1234
it would be something like:
http://mysite.com/subsection/somethingmeaningful.aspx
The problem is that somethingmeaningful.aspx does not exist, because the administrator created it through the web UI, and the content is stored in the database. What I'm thinking is that I'll implement an HTTP handler that handles requests for aspx files. In that handler, I'll check to see if the URL that was requested is an actual file or one of my "fake pages". If it is a request for a fake page, I'll re-route the request to the generic content page for the appropriate section, change the query string to request the appropriate data from the database, and rewrite the URL so that it looks to the user as if the fake page really exists. The problem I'm having right now is that I can't figure out how to route the request to the default handler for aspx pages. I tried to instantiate a PageHandlerFactory, but the constuctor is protected internal. Is there any way for me to tell my HttpHandler to call the HttpHandler that would normal be used to process a request? My handler code currently looks like this:
using System.Web;
using System.Web.UI;
namespace HandlerTest
{
public class FakePageHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
if(RequestIsForFakedPage(context))
{
// reroute the request to the generic page and rewrite the URL
PageHandlerFactory factory = new PageHandlerFactory(); // this won't compile because the constructor is protected internal
factory.GetHandler(context, context.Request.RequestType, GetGenericContentPath(context), GetPhysicalApplicationPath(context)).ProcessRequest(context);
}
else
{
// route the request to the default handler for aspx pages
PageHandlerFactory factory = new PageHandlerFactory();
factory.GetHandler(context, context.Request.RequestType, context.Request.Path, context.Request.PhysicalPath).ProcessRequest(context);
}
}
public string RequestForPageIsFaked(HttpContext context)
{
// TODO
}
public string GetGenericContentPath(HttpContext context)
{
// TODO
}
public string GetPhysicalApplicationPath(HttpContext context)
{
// TODO
}
}
}
I still have some work to do to determine if the request is for a real page, and I haven't rewritten any URLs yet, but is something like this possible? Is there another way to create a PageHandlerFactory other than calling its constructor? Is there any way I can route the request up to the "normal" HttpHandler for an aspx page? I'd basically be saying "process this ASPX request as you normally would."
If you are using 3.5, look into using asp.net routing.
http://msdn.microsoft.com/en-us/library/cc668201.aspx
You would be better off using an http module for this, as in this case you can use the RewritePath method to route the request for fake pages, and do nothing for actual pages which will allow them to be processed as normal.
There is a good explanation of this here which also covers the benefits of using IIS 7.0 if that is an option for you.
I've just pulled this off a similar system we've just written.
This method takes care of physical pages and "fake" pages. You'll be able to ascertan how this fits with your fake page schema, I'm sure.
public class AspxHttpHandler : IHttpHandlerFactory
{
#region ~ from IHttpHandlerFactory ~
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
{
string url=context.Request.Url.AbsolutePath;
string[] portions = url.Split(new char[] { '/', '\\' });
// gives you the path, i presume this will help you identify the section and page
string serverSidePage=Path.Combine(context.Server.MapPath("~"),url);
if (File.Exists(serverSidePage))
{
// page is real
string virtualPath = context.Request.Url.AbsolutePath;
string inputFile = context.Server.MapPath(virtualPath);
try
{
// if it's real, send in the details to the ASPX compiler
return PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);
}
catch (Exception ex)
{
throw new ApplicationException("Failed to render physical page", ex);
}
}
else
{
// page is fake
// need to identify a page that exists which you can use to compile against
// here, it is CMSTaregtPage - it can use a Master
string inputFile = context.Server.MapPath("~/CMSTargetPage.aspx");
string virtualPath = "~/CMSTargetPage.aspx";
// you can also add things that the page can access vai the Context.Items collection
context.Items.Add("DataItem","123");
return PageParser.GetCompiledPageInstance(virtualPath, inputFile, context);
}
public void ReleaseHandler(IHttpHandler handler)
{
}
What is the best practice to support data for asp.net 2.0-3.5 ajax web application?
I don't want to use update panels, just plain text data (JSON).
Should I use web services? Or is there another way.
Errrr... Use an .aspx page ? What are handlers for ?
You just have to create a generic base handler that will take care of json (de)serialization (e.g. using Json.net) and then implement handlers for your ajax calls.
public abstract class JsonHandlerBase<TInput, TOutput> : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "application/json";
TInput input = (TInput)context.Request; // Desesialize input
TOutput output = ProcessRequest(context, parameter);
string json = (string)output; // Serialize output
context.Response.Write(json);
}
public abstract TOutput ProcessRequest(HttpContext context, TInput input);
public bool IsReusable { get { return false; } }
}
This is just an example, it's up to you to decide want you need in your base handler.
You can use plain aspx pages or handlers and just output JSON. You do this by erasing all the Html in the aspx and then using Response.Write() in the code.
Then for the front end JS you can use JQuery or any other Ajax framework.
You may also want to check out Asp.Net MVC. MVC has a JsonResult resonse type and is very easy to use together with JQuery to get very good Ajax functionality.
I have an ASP.Net application and I've noticed through using profilers that there is a sizable amount of processing that happens before my page even runs. In my application, we don't used viewstate, asp.Net session, and we probably don't require most of the overhead that comes of a consequence of using the asp.net page lifecycle. Is there some other class I can easily inherit from that will cut out all the Asp.Net stuff, and just let my handle writing to the page by myself?
I've heard that ASP.Net MVC can cut down page loads considerably, simply because it doesn't use the old asp.net lifecycle, and handles pages differently. Is there an easy way, maybe by simply having my web pages inherit some other class to take advantage of something like this. I would like a solution that works in ASP.Net 2.0 if at all possible.
Most articles I found where talking about using the Page as a base class and implementing functionality on top of that, it looks like you need to make your own MyPage class that implements IHttpHandler
From the MSDN article
using System.Web;
namespace HandlerExample
{
public class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
context.Response.Write("This is an HttpHandler Test.");
context.Response.Write("Your Browser:");
context.Response.Write("Type: " + context.Request.Browser.Type + "");
context.Response.Write("Version: " + context.Request.Browser.Version);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
}
}
Again, from the article: To use this handler, include the following lines in a Web.config file.
<configuration>
<system.web>
<httpHandlers>
<add verb="*" path="handler.aspx" type="HandlerExample.MyHttpHandler,HandlerTest"/>
</httpHandlers>
</system.web>
</configuration>
I'd take a look at the source code for System.web.ui.page and take a look at what it does to guide you. My guess is that it mostly just calls the different methods in the asp.net page lifecycle in the right order. You'd do something similar by calling your own page_load from the ProcessRequest method. That would route to your individual implementation of your classes that implement your MyPage.
I've never thought of doing something like this before and it sounds pretty good since I really don't use any of the bloated webforms functionality. MVC may make this whole exercise futile, but it does seem pretty neat.
My Quick Example
new base:
using System.Web;
namespace HandlerExample
{
// Replacement System.Web.UI.Page class
public abstract class MyHttpHandler : IHttpHandler
{
// Override the ProcessRequest method.
public void ProcessRequest(HttpContext context)
{
// Call any lifecycle methods that you feel like
this.MyPageStart(context);
this.MyPageEnd(context);
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
// define any lifecycle methods that you feel like
public abstract void MyPageStart(HttpContext context);
public abstract void MyPageEnd(HttpContext context);
}
Implementation for the page:
// Individual implementation, akin to Form1 / Page1
public class MyIndividualAspPage : MyHttpHandler
{
public override void MyPageStart(HttpContext context)
{
// stuff here, gets called auto-magically
}
public override void MyPageEnd(HttpContext context)
{
// stuff here, gets called auto-magically
}
}
}
If you don't need all this "asp.net stuff", you may wish to implement a custom IHttpHandler. Afaik, there are no other standard IHttpHandlers to reuse except the Page class.
To do this you should start by looking at the System.Web.UI.PageHandlerFactory class and the corresponding System.Web.IHttpHandlerFactory interface.
From there you will probably look at the System.Web.IHttpHandler inferface and the System.Web.UI.Page class.
Basically you will write your own IHttpHandlerFactory that generates IHttpHandlers that handles the page requests.