RouteTable.Routes.MapPageRoute with optional parameter - asp.net

I'm in an ASP.Net 4.0 web forms app.
I have this routing already in place in Global.asax and working:
RouteTable.Routes.MapPageRoute("status", "members/{userid}/{status}", "~/members/status.aspx");
The 'status' parameter will let me set active tab on the page to either status, blog, photos or about (default is status).
So when I post to: /members/status.aspx?userId=first.last
I'll see /members/first.last/status when the page loads.
What I need to do is render some links that will take user to the blog tab with a blog id so I can land on blog tab, which has multiple blogs displayed, and scroll to specific blog by id. I'm trying to set the href similar to how we do it in MVC: /members/first.last/blog/1000 (where 1000 is blog Id) - my other attempt is /members/first.last/blog/?id=1000 but I can never see the Id in the querystring keys in Page_Load.
I've tried adding this routing to no avail:
RouteTable.Routes.MapPageRoute("status",
"members/{userid}/{status}/{*queryvalues}", "~/members/status.aspx",
false,
new RouteValueDictionary { { "id", #"\d{4}" } });
Thanks for any assistance.

I think you should look in the items collection.
this.Context.Items["userid"]
or in RoutData collection for 4.0
Page.RouteData.Values["userid"]
see this link for info How to: Access URL Parameters in a Routed Page

Use as below:
routes.MapPageRoute(
"ProductsBrowse",
"browse/{BrowseBy}/{Category}/{*queryvalues}",
"~/Pages/Products/Browse.aspx"
);
Thx: asp.net webforms routing: optional parameters

Related

ASP.NET Routing optional first parameter

I am new ASP.NET Routing and have a question.
On the site I am developing, visitors sign up and create an account (with a display name), such as JohnDoe and the site personalises itself, with information pertaining to the display name.
For example (in order of the routing table, I currently have):
http://www.domain.com/ - Generic index page
http://www.domain.com/Page1 - Generic page1
http://www.domain.com/Page2 - Generic page2
http://www.domain.com/JohnDoe - Personalised index page
http://www.domain.com/JohnDoe/Page1 - Personalised page1
http://www.domain.com/JohnDoe/Page2 - Personalised page2
The above is working fine (I simply have a rule for every eventuality).
My problem is that, I now need to create asp:HyperLink controls on the pages of the site. Currently, I am using this:
NavigateUrl="<%$ RouteUrl:RouteName=ROUTENAME %>"
....in the tag of the HyperLink control
How can I, given the following addresses....
http://www.domain.com/Example - Generic example page
http://www.domain.com/JohnDoe/Example - Personalised example page
....match ROUTENAME, where the first parameter can be optional?
I could obviously create two routes, as follows....
Routes.MapPageRoute("Example_No_Displayname", "Example", "~/Example.aspx")
Routes.MapPageRoute("Example_With_Displayname", "{Code}/Example", "~/Example.aspx")
....but that means that the route names need to be different and I need to bind the Hyperlink control to match both of them.
I hope I have made myself clear and I appreciate any assistance the community may be able to give.
It would be somewhat atypical to put the username itself in the URL. The actual user identity/name would typically be extracted from some established security context/token, such as a cookie, so you could use the same URLs for both the anonymous pages and the personalized pages. Is there a specific reason you want to have the username in the URL?
You shouldn't need to match directly on the route name if you use the GetRouteUrl method. That way you can still use the two routes you need to make this work. See the full sample here.

ASP.net 4.0 Webforms Routing Postback Issue

We are using asp.net 4.0 and routing with web forms to create friendly urls.
The routing is working fine except that the correct "action" value is not being assigned to the form element in the master page for any route that has multiple levels.
For example, the route ( customer/{customerid} ) when browsed to with .../customer/12345 only displays 12345 in the "action" attribute of the form. The issue with this is that it isn't complete and any postback fails and gives an error "The HTTP verb POST used to access path is not allowed" If I updates the action as "customer/12345" (using Firebug), the postback works fine.
It even errors when using static routes like customer/customer, it only puts "customer" and not "customer/customer" as the action value of the form. Basically, only putting the last piece of the route into the action attribute instead of the whole route. Why?
Any ideas on how to correct this?
You can work around this by overiding the form action as form1.Action = Request.Url.PathAndQuery;]in the Page_Load event
See this related Topic.
It uses Request.RawUrl instead of Request.Url.PathAndQuery, which seams to return the same value.

Get the virtual url when using routing with webforms

How do I get the virtual url and not the webforms page name that the url is mapped to when using System.Web.Routing with webforms.
I want the url "/confirm/5" and not "Confirm.aspx".
To get the routed URL for the page you are currently visiting, use Request.Url, as Pavel notes.
If you need to get the routed URL for a different page (such as when creating a hyperlink to another page), use the Page.GetRouteUrl method.
Here's a code snippet showing the use of Page.GetRouteUrl. It's from my article, URL Routing in ASP.NET 4:
lnkCategory.NavigateUrl = Page.GetRouteUrl("View Category", new { CategoryName = "Beverages" });
In the above snippet, "View Category" is the name of the routing rule I want to use. CategoryName is one of the routing parameters and I want to use the value "Beverages". The above call to Page.GetRouteUrl returns the string "/Categories/Beverages". (Of course, the exact string is returns depends on the routing rule "View Category" and the parameter values, but hopefully you get the idea.)
Try the following: System.Web.HttpContext.Current.Request.Url.AbsolutePath

ASP.NET MVC output cache with dynamic fragment

How could I cache an entire page except a bit at the top which says something along the lines of "Welcome back, Matt! | Log Out" if the user is logged in and so-on?
I'm using ASP.NET MVC 2.
What you are trying to achieve is called donut-caching or cache substitution. As of ASP.NET MVC 2 there is no built in helper to support this scenario. As much as I know it was a planned feature in MVC v.1 but it was dropped somewhere in the way to the release. For more info check this links http://haacked.com/archive/2008/11/05/donut-caching-in-asp.net-mvc.aspx, Is Donut Caching working properly with ASP.NET MVC?.
VaryByParam option that is mentioned by Oleg here is not a good idea in your case. If you have VaryByParam a different version of the page will be put in the cache for every different value of the parameter (in your case for every user-name).
Personally I would think of caching the data, not the whole output of the page.
Probably helps
<%# OutputCache Duration="15" VaryByParam="*" %>
or with some other value for the VaryByParam. See http://msdn.microsoft.com/en-us/library/hdxfb6cy.aspx, http://blog.maartenballiauw.be/post/2008/06/Creating-an-ASPNET-MVC-OutputCache-ActionFilterAttribute.aspx and http://blogs.microsoft.co.il/blogs/gilf/archive/2010/07/18/asp-net-output-cache-provider.aspx.
Moreover, if you have the start page which is not user depended, it is possible to replace the start page with a very static welcome page with the empty field (hidden div) instead of "Welcome back, Matt! | Log Out". After that an ajax request for filling of the user name can be started at the client side. This kind of the welcome page page can be very good cached.
Not Supported != Not Possible
http://blog.maartenballiauw.be/post/2008/07/01/Extending-ASPNET-MVC-OutputCache-ActionFilterAttribute-Adding-substitution.aspx
http://www.klopfenstein.net/lorenz.aspx/output-donut-caching-attribute-asp-net-mvc-partial-requests
http://haacked.com/archive/2009/05/12/donut-hole-caching.aspx
Here you have a workaround solution:
*Add the OuptutCache attribute to the Controller that manages the whole view as usually:
[OutputCache(Duration = 3600, VaryByParam = "*")]
public ActionResult Index(FormCollection formCollection)
{
//Controller code
}
*For the part that you don't want to do caching, load it using jquery + an ajax request (with its own controller and without the OutputCache attribute):
<div id="loginContainer"></div>
$(document).ready(function() {
$.post('controller/action', postdata, function(data) {
if (data.success == true) {
//Populate the container with the obtained data
}
});
});
The view will be retrieved from the Output Cache and, once it is loaded, a request to obtain the login info will be performed. Hopefully, it will be a very quick request and the user will not notice the delay.
Get this via nuget:
http://mvcdonutcaching.codeplex.com/
Add an action for LogOnPArtial, so you can change it from Html.Partial to Html.Action in the _Layout.cshtml
#Html.Action("_LogOnPartial","Account",true)
The true is a exclude parameter that says, exclude this from caching. The action will be called even if the page it is in is cached. This is the "hole" in the donut that is not cached.
On your page, such as About.cshtml that you want cached, apply DonutOutputCache attribute. This allows the new framework to inspect the page as it's caching it, and add flags where you've excluded actions.
The nice thing is the _LogOnPartial is not cached and will refresh for different users while the rest of the page is cached and the About() action will not be run. You could even configure caching on the _LogOnPartial action you created using the DonutOutputCache attribute, but a more frequent or less frequent interval, or vary by some other param. This allows you to compose pages of partials, and the cache refreshing logic is independently configured for each partial.
IMO this tool is exactly how I imagined caching in MVC should have been implemented.

url rewriting + Asp.Net Login Form = Death

on our site we do url rewriting to generate massive amounts of database generated pages. on every page, there is a Login control for users. like this:
Internal aspx page: /DB.aspx?id=123
User visible url: /ABC/123.aspx, /ABC/456.aspx ... (url rewritten)
unfortunately, the tag on each page has an action attribute of "DB.aspx?id=123". when the user clicks the button the browser is posting to /ABC/DB.aspx?id=123 which of course does not exist.
solutions i tried:
1. change the action attribute by subclassing HtmlForm. this destroys the all other forms on the site.
2. remove the action attribute (so that the browser is always posting to the same url). this works on the rewritten pages but on "/" (the default.aspx in the root dir) i get a message that the verb post is not allowed on "/" (iis 6 and i have no control over mappings)
anybody?
Check this really nice blog post from scott gu, http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx.
"Specifically, you can take advantage of the new ASP.NET 2.0 Control Adapter extensibility architecture to customize the rendering of the control, and override its "action" attribute value with a value you provide. This doesn't require you to change any code in your .aspx pages"
Check the section: "Handling ASP.NET PostBacks with URL Rewriting", I have used the adapter he posted successfully.
Ps. be aware there are some issues on asp.net when using url rewrite when using cookieless session, and the rewritten url is deeper than the original page, just like the one you have. (/abc/apage vs. /db?). The issue is right into the source code of the framework, there are workarounds but that's a whole subject (with tradeoffs :( ... you might want to have them at the same level).
Semantics maybe, but does the action attribute = "DB.aspx?id=123" or "/DB.aspx?id=123"? Assuming your URL rewriting allows pass-through to physical pages, this might be your issue.
I never did it, but I saw the code using Reflector and I guess you can fix it this way:
On the page:
this.Form.Action = null;
or:
this.Form.SetAttribute("action", null);
If that doesn't work, just set the path you want:
this.Form.SetAttribute("action", "ABC/123.aspx");
If you upgrade to ASP.NET 3.5 SP1, the action property is now properly recognized and can be set from codebehind.

Resources