Mapping RouteTable on PreInit() - Will it cause any problems? - asp.net

My page route mapping changes depending on certain criteria. To determine this criteria I need access to HttpRequest which means I can't do my route mapping in Application_Start(). On that note I have done it on PreInit() on my default page and it seems to work without an issue. However, all the examples I have seen with Route Tables are doing it in Application_Start, is this purely to avoid clearing the route list and adding them again? Will there it cause any harm to my web application doing in the way I am doing it?
EDIT: Example:
I load controls from different folders based on the project number and whether the website is being viewed on a mobile device, on that note I need to know this information before mapping my routes, like so:
RouteCollection.MapPageRoute("OneParam", "{Action}.html", String.Format("~/{1}{2}/Default.aspx", ProjectNumber, MobilePathStr));
which would map to something like ~/1234/Mobile/Default.aspx or could map to ~/1234/Default.aspx.

Don't do that.
Instead, you should create your own RouteBase class which looks up that information for each request and runs the appropriate handler.
To send to an ASPX page, you can either return BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page)) as Page; or create a PageRouteHandler and delegate to it.

Related

JSF page forward - slow CSS [duplicate]

I am currently learning JSF and was rather amazed and puzzled when I realized that whenever we use <h:form>, the standard behavior of JSF is to always show me the URL of the previous page in the browser, as opposed to the URL of the current page.
I understand that this has to do with the way JSF always posts a form to the same page and then just renders whatever page the controller gives it back to the browser which doesn't know the page location has changed.
It seems like JSF has been around for long enough that there must be a clean, solid way to deal with this. If so, would you mind sharing?
I have found various workarounds, but sadly nothing that seems like a real solid solution.
Simply accept that the URL is misleading.
Append "?faces-redirect=true" to the return value of every bean's action and then
figure out how to replace #RequestScoped with something else (Flash Scopes, CDI conversation, #SessionScoped, ...).
accept to have two HTTP round trips for every user action.
Use some method (e.g. 3rd party library or custom code) to hide the page name in the URL, always using the same generic URL for every page.
If "?faces-redirect=true" is as good as it gets, is there a way do configure an entire application to treat all requests this way?
Indeed, JSF as being a form based application targeted MVC framework submits the POST form to the very same URL as where the page with the <h:form> is been requested form. You can confirm it by looking at the <form action> URL of the generated HTML output. This is in web development terms characterized as postback. A navigation on a postback does by default not cause a new request to the new URL, but instead loads the target page as content of the response. This is indeed confusing when you merely want page-to-page navigation.
Generally, the right approach as to navigation/redirection depends on the business requirements and the idempotence (read: "bookmarkability") of the request (note: for concrete code examples, see the "See also" links below).
If the request is idempotent, just use a GET form/link instead of POST form (i.e. use <a>, <form>, <h:link> or <h:button> instead of <h:form> and <h:commandXxx>).
For example, page-to-page navigation, Google-like search form, etc.
If the request is non-idempotent, just show results conditionally in the same view (i.e. return null or void from action method and make use of e.g. <h:message(s)> and/or rendered).
For example, in-page data entry/edit, multi-step wizard, modal dialog, confirmation form, etc.
If the request is non-idempotent, but the target page is idempotent, just send a redirect after POST (i.e. return outcome with ?faces-redirect=true from action method, or manually invoke ExternalContext#redirect(), or put <redirect/> in legacy XML navigation case).
For example, showing list of all data after successful editing, redirect after login, etc.
Note that pure page-to-page navigation is usually idempotent and this is where many JSF starters fail by abusing command links/buttons for that and then complain afterwards that URLs don't change. Also note that navigation cases are very rarely used in real world applications which are developed with respect to SEO/UX and this is where many JSF tutorials fail by letting the readers believe otherwise.
Also note that using POST is absolutely not "more secure" than GET because the request parameters aren't immediately visible in URL. They are still visible in HTTP request body and still manipulatable. So there's absolutely no reason to prefer POST for idempotent requests for the sake of "security". The real security is in using HTTPS instead of HTTP and checking in business service methods if currently logged-in user is allowed to query entity X, or to manipulate entity X, etc. A decent security framework offers annotations for this.
See also:
What is the difference between redirect and navigation/forward and when to use what?
JSF implicit vs. explicit navigation
What URL to use to link / navigate to other JSF pages
Bookmarkability via View Parameters feature
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
When should I use h:outputLink instead of h:commandLink?
Creating master-detail pages for entities, how to link them and which bean scope to choose
Retaining GET request query string parameters on JSF form submit
Pass an object between #ViewScoped beans without using GET params

Dynamically disable/enable outputcache for a page during runtime

I am using a CMS system, by the name of Composite C1. It renders all of it's content through single page (Page.aspx), which has a custom output cache profile attached.
This is all good, but I have run into a problem.
I want to have caching, but there are certain URLs that I would like to disable outputcaching for.
I know there is the varybycustom attribute that I can add to the cache profile, but I don't think this will give me exactly what I want. I want to be able to disable the cache completely when hitting specific URLs (or perhaps some other condition).
This seems to be very tricky as every page/url renders through the single Page.aspx file with it's outputcache profile defined.
Does anyone have any advice on how I might be able to solve this problem?
In \Global.asax you'll find an override of GetVaryByCustomString that calls into Composite C1 to evaluate if the response should be cached or not. You could intervene that, and only call into Composite if the request is not for one of the urls that you don't want cached:
if (context.Request.Url.AbsolutePath != "/dont-cache-this")
{
return ApplicationLevelEventHandlers.GetVaryByCustomString(context, custom) ?? base.GetVaryByCustomString(context, custom);
}
return null;
Note that upgrading Composite C1 later on might replace \Global.asax and wipe your changes.
Also see http://msdn.microsoft.com/en-us/library/5ecf4420.aspx
You can insert the following code to functions which are on the pages that shouldn't be cached. Or add a small function that would do just that:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);

Best way to generate url via Page.GetRouteUrl in a helper class?

I have just added routing in a new asp.net 4 web forms application, and have got my route table set up ok and page requests are working fine.
What I now want to do is use the Page.GetRouteUrl method whenever I need to generate one of my seo friendly Url's. I started to implement this across various pages then thought it might cut down on code a bit if I had a method in one of my own helper classes, that constructs this url (using the Page.GetRouteUrl method) as I might have several parameters that need to be specified against the Page.GetRouteUrl method each time.
However, within my helper class, it doesn't know what 'Page' is. I was thinking I could pass in 'Page' from the page that wants a routed url to be generated, but in some cases I want to construct one of these Url's in another class which doesn't know what 'Page' is, and in that scenario wouldn't be able to pass it in as a param (and therefore wouldn't be able to use the Page.GetRouteUrl within my helper class).
What is the normal approach when wanting to use Page.GetRouteUrl within classes that don't have an instance of the Page object?
You can pass Page as a constructor parameter, or this is actually the page reference:
var page = HttpContext.Current.Handler as Page;
You can cast it to the page type. Depending on the type of requests you are making, it may not always be page (say from a web service call, etc.).
HTH.

Intercept Page object creation to hook up events

I'm looking for a way to intercept the ASP.NET processing pipeline in such a way to be able to register event handlers to all events on the Page class. The reason is, I need to maintain a session-bound instance of a component that needs to be notified of all important Page events, starting from OnPreInit.
There's an arbitrary number of Page descendants in the application, which are not under my control. Hence I cannot use an approach like using a single custom descendant, that would notify the session-bound component, as a base class for all pages in the web application.
I don't think creating a custom IHttpHandler or IHttpModule implementation would solve the problem. Also note I cannot create a custom HttpApplication descendant.
It isn't going to be an elegant process to do what you are looking at, especially if you need to handle multiple page events, but in theory it is fully possible from within the Global.asax to setup handlers that you need for each and every page.
The trick here is to add your code to the global.asax in the PreRequestHandlerExecute method, from here you can get access to the HttpApplication object, get access to the page from there, and then register your events. This process is necessary as a new page instance is created for every page that is processed.
Now, other options as you know are far more elegant, but this should get to where you need to be. One helpful tutorial I found although around Themeing shows you the whole process here.
EDIT:
After seeing your comment, yes, you can simply do what I'm stating above, in a custom HttpModule. The article I linked even shows you that process :)
Without knowing more about what you're trying to accomplish it really sounds like you do indeed want to create a http module or handler. You might want to take a look at this question

ASP.Net Routing with WebForms

I am trying to cut over an existing WebForms app to use Routing, and want to do it in phases. However, I am running into some issues with a particular route:
//I want to catch existing calls to .aspx pages, but force them through
// a route, so I can eventually drop the .aspx extension
new Route("{page}.aspx", new MyCustomRoute());
This isn't working at all, as calls to [SomePage].aspx are never tripping this route... If I change the route to look like this:
//Same thing sans .aspx extension
new Route("{page}", new MyCustomRoute());
All calls to [SomePage] are getting picked up. Any ideas?
Ok, so now I feel stupid...
Turns out there is this little property on the RouteCollection class called RouteExistingFiles which is false by default. Apparently ASP.Net routing gives precedence to existing files before turning them over to routing, so any calls to existing pages would obviously not be handled by my routes. Setting this property to true resolves my issue, all though it may have unintended side effects to which I am as of yet unaware.

Resources