Get url from MvcSiteMapProvider by key with parameter - asp.net-mvc

I usually get the url from a node with the following code:
MvcSiteMapProvider.SiteMaps.Current.FindSiteMapNodeFromKey("AccountDetailsKey").Url
Now I have a node with a parameter. But how can I get the url for that node. So in the below example we need the url of the "AccountDetailsKey" node.
<mvcSiteMapNode key="BeherenAccountKey" title="Zoeken accounts" controller="BeherenAccount" action="BeherenAccount">
<mvcSiteMapNode key="AccountDetailsKey" title="Account details" controller="BeherenAccount" action="AccountDetails" preservedRouteParameters="gebruikersnaam" visibility="!SiteMapHelper,!MainMenu,*" />
</mvcSiteMapNode>
Details action:
[Route("account/details/{gebruikersnaam}")]
public ActionResult AccountDetails(string gebruikersnaam)
{
return this.View((object)gebruikersnaam);
}
De routeconfig:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
Offcourse we could use the following for generating an url to the details page with regular MVC razor code:
#Url.Action("AccountDetails", "BeherenAccount", new { gebruikersnaam = User.Identity.Name.ToLower().Replace("domain\\", "") })
The thing is that we don't want to use action and controller names in our code. We want to generate all the urls from the sitemap. Everything works fine in the whole application but can't get this details page working. We don't have a DynamicNodeProvider because we don't care for separate titles, seo etc because it is an administrator application.
The application shows the loggedin user in the main menu (_Layout page) and this is an url to his own detail page.
So when the loggedin user has username "abc", the details url must be "account/details/abc".

When using preservedRouteParameters, the SiteMap does not automatically generate the route values that are supposed to be placed onto the node. They come from the current request of the page. Normally, when doing this you are expected to provide URLs using ActionLink, Action, RouteLink, or UrlHelper, as would typically be the case when doing CRUD operations, since the links would need to be derived from the database data.
#Html.ActionLink("Gebruikersnaam", "AccountDetails", "BeherenAccount", new { gebruikersnaam = "Value" }, null)
Alternatively, if no route value named gebruikersnaam exists in the current request, you can supply it manually.
string url;
var node = MvcSiteMapProvider.SiteMaps.Current.FindSiteMapNodeFromKey("AccountDetailsKey");
if (node != null)
{
node.RouteValues["gebruikersnaam"] = "Value";
url = node.Url;
}
When you set a route value outside of an ISiteMapNodeProvider or dynamic node provider, the value that is set will only last for the lifetime of the current request.
NOTE: I really don't understand the exact problem you are having with the above code. In my test project it is working fine regardless of what page you are on. If you want to investigate further, you can use the code from Controlling URL Behavior to mimic how the SiteMapNodeUrlResolver uses the MVC UrlHelper.

Related

Default Routing Not working in .NET MVC 1 (and 2)

My default routes are very simple, but the page doesn't properly load without fully qualifying the entire route.
Here are the routes I'm using:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
Here's the only action in the application in a HomeController:
public ActionResult Index()
{
return Content("New stuff");
}
With these URLs:
http://localhost:8081/NewMvc1/
I get The incoming request does not match any route.
With:
http://localhost:8081/NewMvc1/Home
http://localhost:8081/NewMvc1/Home/Index
I get a 404 Mvc page that says it tried to handle the request with a static file.
Yet, finally with a 'fully qualified url'
http://localhost:8081/NewMvc1/Home/Index/1
I get the expected result output from the one and only one action.
New Stuff
This doesn't seem right at all. I've also been getting Failed to Execute Action from this same application (not sure if that's related).
I've used Phil Haack's RouteDebugger to get this far, which pointed out that it wasn't matching the URL when the Optional parameters were missing, but did when those parameters were provided.
You're missing the id from your defaults:
new { controller = "Home", action = "Index", id = UrlParameter.Optional }

MVC3 Custom routing

I developed a website with MVC and I have a little problem on how link addresses appears in the address bar.
When I open the website, I have to log on first; after I log on into the account, the home page appear, but in the browser address bar I still have
http://localhost:1413/Account/LogOn
instead of
http://localhost:1413/Home
Also, after I log out, I am redirected to the log in page, but in the address bar it appears
http://localhost:1413/Account/LogOn
I would like to be just
http://localhost:1413/Account/LogOff
My Global.asx code
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
I used this type of redirection, but the result is the same:
public ActionResult LogOn()
{
if (HttpContext.User.Identity.IsAuthenticated == true)
{
return RedirectToAction("Index", "Home");
}
return View();
}
you need to use redirect action to home page like this
RedirectToAction("Action", "Controller")
so when user has been authenticated you need to redirect the user to the particular controller.
Same goes for the when user logged off
As I can see, your problem is not the routing cause your routing is working fine. Your problem is that you work in the controller Account. That's why account always appears in you url. If you want to get this:
http://localhost:1413/Home
You must link to a controller which you called HomeController.cs
I hope you understand what i tried to explain.
Maybe this can help you more:
http://www.codeproject.com/Articles/190267/Controllers-and-Routers-in-ASP-NET-MVC-3

ASP.NET MVC 2 One Route Works, One Route Doesn't

I have two routes in my ASP.NET MVC application.
The first is working fine - it's an ActionResult that returns a view.
The second is on the same controller and is an ActionResult that returns a Json response. It takes a couple of additional paramaters.
This second route is working on my dev machine, but when I deploy it to the server I get back a blank response. Any suggestions will be gratefully received.
I have also copy-pasted the route into a browser to eliminate any issues in the jQuery JavaScript.
The method
[HttpGet]
public ActionResult CheckSku(string id, string brand) {
CheckSkuModel model = new CheckSkuModel();
model.Id = id;
model.Brand = brand;
return Json(model, JsonRequestBehavior.AllowGet);
}
The routes
routes.MapRoute(
"Default", // Route name
"{controller}.mvc/{action}/{id}", // URL with parameters
new {
controller = "Orders", action = "Send", id = ""
} // Parameter defaults
);
routes.MapRoute(
"CheckSku", // Route name
"{controller}.mvc/{action}/{id}/{brand}", // URL with parameters
new {
controller = "Orders", action = "CheckSku", id = "", brand = ""
} // Parameter defaults
);
Two thing you can quickly check that may help:
1. Swap the two routes around so "CheckSku" is above "default"
2. Make the "CheckSku" more specific so will look something like:
routes.MapRoute(
"CheckSku", // Route name
"Orders.mvc/CheckSku/{id}/{brand}", // URL with parameters
new {
controller = "Orders", action = "CheckSku", id = "", brand = ""
} // Parameter defaults
);
that was another controller doesn't pick up the url by mistake.
An alternative is the use the routelink helper when generating the url so it points to the correct route.
When you say you "get back a blank response", do you mean you're not getting an error, but that the server isn't returning anything? What makes you think this is a routing issue?
Some troubleshooting tips:
Use Fiddler to examine the HTTP request you're making and the raw HTTP response the server is sending back. If you're getting a 500 response code then check the response for error information.
Can you attach a remote debugger to the server? This might give you a chance to investigate any exceptions that are raised.

ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions

We're currently running on IIS6, but hoping to move to IIS 7 soon.
We're moving an existing web forms site over to ASP.Net MVC. We have quite a few legacy pages which we need to redirect to the new controllers. I came across this article which looked interesting:
http://blog.eworldui.net/post/2008/04/ASPNET-MVC---Legacy-Url-Routing.aspx
So I guess I could either write my own route handler, or do my redirect in the controller. The latter smells slightly.
However, I'm not quite sure how to handle the query string values from the legacy urls which ideally I need to pass to my controller's Show() method. For example:
Legacy URL:
/Artists/ViewArtist.aspx?Id=4589
I want this to map to:
ArtistsController Show action
Actually my Show action takes the artist name, so I do want the user to be redirected from the Legacy URL to /artists/Madonna
Thanks!
depending on the article you mentioned, these are the steps to accomplish this:
1-Your LegacyHandler must extract the routes values from the query string(in this case it is the artist's id)
here is the code to do that:
public class LegacyHandler:MvcHandler
{
private RequestContext requestContext;
public LegacyHandler(RequestContext requestContext) : base(requestContext)
{
this.requestContext = requestContext;
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
string redirectActionName = ((LegacyRoute) RequestContext.RouteData.Route).RedirectActionName;
var queryString = requestContext.HttpContext.Request.QueryString;
foreach (var key in queryString.AllKeys)
{
requestContext.RouteData.Values.Add(key, queryString[key]);
}
VirtualPathData path = RouteTable.Routes.GetVirtualPath(requestContext, redirectActionName,
requestContext.RouteData.Values);
httpContext.Response.Status = "301 Moved Permanently";
httpContext.Response.AppendHeader("Location", path.VirtualPath);
}
}
2- you have to add these two routes to the RouteTable where you have an ArtistController with ViewArtist action that accept an id parameter of int type
routes.Add("Legacy", new LegacyRoute("Artists/ViewArtist.aspx", "Artist", new LegacyRouteHandler()));
routes.MapRoute("Artist", "Artist/ViewArtist/{id}", new
{
controller = "Artist",
action = "ViewArtist",
});
Now you can navigate to a url like : /Artists/ViewArtist.aspx?id=123
and you will be redirected to : /Artist/ViewArtist/123
I was struggling a bit with this until I got my head around it. It was a lot easier to do this in a Controller like Perhentian did then directly in the route config, at least in my situation since our new URLs don't have id in them. The reason is that in the Controller I had access to all my repositories and domain objects. To help others this is what I did:
routes.MapRoute(null,
"product_list.aspx", // Matches legacy product_list.aspx
new { controller = "Products", action = "Legacy" }
);
public ActionResult Legacy(int catid)
{
MenuItem menuItem = menu.GetMenuItem(catid);
return RedirectPermanent(menuItem.Path);
}
menu is an object where I've stored information related to menu entries, like the Path which is the URL for the menu entry.
This redirects from for instance
/product_list.aspx?catid=50
to
/pc-tillbehor/kylning-flaktar/flaktar/170-mm
Note that RedirectPermanent is MVC3+. If you're using an older version you need to create the 301 manually.

Asp.Net MVC: How do I enable dashes in my urls?

I'd like to have dashes separate words in my URLs. So instead of:
/MyController/MyAction
I'd like:
/My-Controller/My-Action
Is this possible?
You can use the ActionName attribute like so:
[ActionName("My-Action")]
public ActionResult MyAction() {
return View();
}
Note that you will then need to call your View file "My-Action.cshtml" (or appropriate extension). You will also need to reference "my-action" in any Html.ActionLink methods.
There isn't such a simple solution for controllers.
Edit: Update for MVC5
Enable the routes globally:
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapMvcAttributeRoutes();
// routes.MapRoute...
}
Now with MVC5, Attribute Routing has been absorbed into the project. You can now use:
[Route("My-Action")]
On Action Methods.
For controllers, you can apply a RoutePrefix attribute which will be applied to all action methods in that controller:
[RoutePrefix("my-controller")]
One of the benefits of using RoutePrefix is URL parameters will also be passed down to any action methods.
[RoutePrefix("clients/{clientId:int}")]
public class ClientsController : Controller .....
Snip..
[Route("edit-client")]
public ActionResult Edit(int clientId) // will match /clients/123/edit-client
You could create a custom route handler as shown in this blog:
http://blog.didsburydesign.com/2010/02/how-to-allow-hyphens-in-urls-using-asp-net-mvc-2/
public class HyphenatedRouteHandler : MvcRouteHandler{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.RouteData.Values["controller"] = requestContext.RouteData.Values["controller"].ToString().Replace("-", "_");
requestContext.RouteData.Values["action"] = requestContext.RouteData.Values["action"].ToString().Replace("-", "_");
return base.GetHttpHandler(requestContext);
}
}
...and the new route:
routes.Add(
new Route("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Default", action = "Index", id = "" }),
new HyphenatedRouteHandler())
);
A very similar question was asked here: ASP.net MVC support for URL's with hyphens
I've developed an open source NuGet library for this problem which implicitly converts EveryMvc/Url to every-mvc/url.
Uppercase urls are problematic because cookie paths are case-sensitive, most of the internet is actually case-sensitive while Microsoft technologies treats urls as case-insensitive. (More on my blog post)
NuGet Package: https://www.nuget.org/packages/LowercaseDashedRoute/
To install it, simply open the NuGet window in the Visual Studio by right clicking the Project and selecting NuGet Package Manager, and on the "Online" tab type "Lowercase Dashed Route", and it should pop up.
Alternatively, you can run this code in the Package Manager Console:
Install-Package LowercaseDashedRoute
After that you should open App_Start/RouteConfig.cs and comment out existing route.MapRoute(...) call and add this instead:
routes.Add(new LowercaseDashedRoute("{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = UrlParameter.Optional }),
new DashedRouteHandler()
)
);
That's it. All the urls are lowercase, dashed, and converted implicitly without you doing anything more.
Open Source Project Url: https://github.com/AtaS/lowercase-dashed-route
Here's what I did using areas in ASP.NET MVC 5 and it worked liked a charm. I didn't have to rename my views, either.
In RouteConfig.cs, do this:
public static void RegisterRoutes(RouteCollection routes)
{
// add these to enable attribute routing and lowercase urls, if desired
routes.MapMvcAttributeRoutes();
routes.LowercaseUrls = true;
// routes.MapRoute...
}
In your controller, add this before your class definition:
[RouteArea("SampleArea", AreaPrefix = "sample-area")]
[Route("{action}")]
public class SampleAreaController: Controller
{
// ...
[Route("my-action")]
public ViewResult MyAction()
{
// do something useful
}
}
The URL that shows up in the browser if testing on local machine is: localhost/sample-area/my-action. You don't need to rename your view files or anything. I was quite happy with the end result.
After routing attributes are enabled you can delete any area registration files you have such as SampleAreaRegistration.cs.
This article helped me come to this conclusion. I hope it is useful to you.
Asp.Net MVC 5 will support attribute routing, allowing more explicit control over route names. Sample usage will look like:
[RoutePrefix("dogs-and-cats")]
public class DogsAndCatsController : Controller
{
[HttpGet("living-together")]
public ViewResult LivingTogether() { ... }
[HttpPost("mass-hysteria")]
public ViewResult MassHysteria() { }
}
To get this behavior for projects using Asp.Net MVC prior to v5, similar functionality can be found with the AttributeRouting project (also available as a nuget). In fact, Microsoft reached out to the author of AttributeRouting to help them with their implementation for MVC 5.
You could write a custom route that derives from the Route class GetRouteData to strip dashes, but when you call the APIs to generate a URL, you'll have to remember to include the dashes for action name and controller name.
That shouldn't be too hard.
You can define a specific route such as:
routes.MapRoute(
"TandC", // Route controllerName
"CommonPath/{controller}/Terms-and-Conditions", // URL with parameters
new {
controller = "Home",
action = "Terms_and_Conditions"
} // Parameter defaults
);
But this route has to be registered BEFORE your default route.
If you have access to the IIS URL Rewrite module ( http://blogs.iis.net/ruslany/archive/2009/04/08/10-url-rewriting-tips-and-tricks.aspx ), you can simply rewrite the URLs.
Requests to /my-controller/my-action can be rewritten to /mycontroller/myaction and then there is no need to write custom handlers or anything else. Visitors get pretty urls and you get ones MVC can understand.
Here's an example for one controller and action, but you could modify this to be a more generic solution:
<rewrite>
<rules>
<rule name="Dashes, damnit">
<match url="^my-controller(.*)" />
<action type="Rewrite" url="MyController/Index{R:1}" />
</rule>
</rules>
</rewrite>
The possible downside to this is you'll have to switch your project to use IIS Express or IIS for rewrites to work during development.
I'm still pretty new to MVC, so take it with a grain of salt. It's not an elegant, catch-all solution but did the trick for me in MVC4:
routes.MapRoute(
name: "ControllerName",
url: "Controller-Name/{action}/{id}",
defaults: new { controller = "ControllerName", action = "Index", id = UrlParameter.Optional }
);

Resources