Im building a dynamic menu in my MVC project so in my controller I have the following:
// list accounts
menu.Add(new Model.Custom.Menu() { Active = true, Url = "dashboard.html", Icon = "mdi mdi-account-card-details", Items = null, Text = "List Accounts" });
Right now dashboard.html link is hardcoded but I required to be #Html.ActionLink so it renders the right path.
Any clue?
You can use the UrlHelper to call the Action method which will return the correct relative path to the action method.
var url=new UrlHelper(Request.RequestContext);
var newUrl = url.Action("Dashboard");
//The above will generate the path to Dashboard action method.
menu.Add(new Model.Custom.Menu() { Active = true, Url = newUrl});
Related
I am using below code to form url :
PricingLink = Url.Action("PricingRequestSummary", "GlobalPricing", new { id = pricingId }, this.Request.Url.Scheme)
Gives me result as :
http://localhost:56287/Admin/GlobalPricing/PricingRequestSummary/4
I want only (Admin i dont want)
http://localhost:56287/GlobalPricing/PricingRequestSummary/4
How to achieve that ?
Thanks
I am assuming you are executing the Url.Action helper method from a view under the Admin area. By default it will use the current area name when building the urls. But you can override this by explicitly passing an empty string as area name.
<a href="Url.Action("PricingRequestSummary", "GlobalPricing",
new { id = pricingId, area = string.Empty }, this.Request.Url.Scheme)">Test</a>
I have a route like this:
context.MapRoute(
"Agents - Default",
"agent/{username}/{action}",
new { controller = "Agent", action = "Profile" }
);
If i do this in my code:
var url = helper.RouteUrl("Agents - Default", new { username })
It generates a URL like this:
"/agent/joebloggs/Details"
Whereas i would expect it to generate a URL like this:
"/agent/joebloggs"
(which resolves/matches the route fine when i hit manually)
If i change it to this:
var url = helper.RouteUrl("Agents - Default", new { username }, action = "Profile" })
It works fine, but i thought the point of the route default is that i don't need to do that.
Any ideas? Where it is pulling this "Details" part from?
The RouteUrl must have enough information to match the route. Route name is just another narrowing specification for the match.
But the main issue you have is that you are not declaring the route values as name-value pairs, so MVC won't be able to convert it to a RouteValueDictionary.
var url = helper.RouteUrl("Agents - Default", new { username = "MyUser" })
Update
i am on a page whose actionname is "Details"
This is your main issue. The default behavior of MVC is to always use any keys that are not set from the current context when generating outgoing URLs. So for example, if your current action is "Details" and you don't set an action when generating your URL, then it will automatically inject a route value action = "Details". Some people consider this "feature" of MVC to be a bug. See work item 1346.
Aside from the solutions that are provided by Microsoft in the above link, another workaround is to create a fake request context to eliminate these extra values, which might be cleaner in your case.
var currentHttpContext = HttpContext.Current;
// Get the route data for the current route
// using the current HTTP context.
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(currentHttpContext));
// Create a URI based on the home page to ensure no query string
// parameters are injected into the URL.
var uri = new Uri(currentHttpContext.Request.Url, "/");
// Create an HTTP request based on the URI
var request = new HttpRequest(string.Empty, uri.ToString(), uri.Query);
// Create a TextWriter with null stream as a backing stream
// which doesn't consume resources
using (var nullWriter = new StreamWriter(Stream.Null))
{
// Create an HTTP response based on a null writer.
var response = new HttpResponse(nullWriter);
// Create a fake HTTP context
var httpContext = new HttpContextWrapper(new HttpContext(request, response));
// Create a fake RequestContext
var requestContext = new RequestContext(httpContext, routeData);
// Use the fake RequestContext to generate the URL without
// unexpectedly injecting the action name of the current request.
var helper = new UrlHelper(requestContext);
var url = helper.RouteUrl("Agents - Default", new { username = "MyUser" });
}
I added routing to my solution in order to have a more user friendly URL in the address bar.
I start the solution and when I rollover my Favorites link, I see the URL .../Affaire/Favorite (picture below). This one is OK for me.
When I rollover my Recycle bin link, I see the URL ../Affaire/Deleted (picture below). This one is OK for me.
Then I click on the Recycle bin link, I navigate to the corresponding page and the URL showed in the address bar is OK for me (picture below).
Next, I rollover the Favorite link again (picture below), I see the URL ../Affaire/Delete?OnlyFavorite=true!! That's not OK.
The routing is now retrieving an attribute not from my link but from the active URL! This attribute is named OnlyFavorite and I don't want this attribute. This is the "reflexion". Notice that all of my routes are using the same controller and the same action but using different attributes for the routes.
Below are some links I used.
Example for navigating to the favorite page:
#Html.ActionLink("Favorites", "SearchAffaires", new { OnlyFavorite = true })
Example for navigating to the recycle bin page:
#Html.ActionLink("Recycle bin", "SearchAffaires", new { StatusCode = "DELETED" })
Here are my routes:
routes.MapRoute(
"Affaire Status Open/Closed/Deleted", // Route name
"{controller}/{StatusCode}", // URL with parameters
new { action = "SearchAffaires" }, // Parameter defaults
new { controller = "Affaire", StatusCode = "^Open$|^Closed$|^Deleted$" }// Contraints
);
routes.MapRoute(
"Affaire Only Favorite", // Route name
"{controller}/Favorite", // URL with parameters
new { action = "SearchAffaires", Page = 1, OnlyFavorite = true }, // Parameter defaults
new { controller = "Affaire" } // Contraints
);
Do you have any idea how can I proceed to avoid this behaviour?
I don't want the routing to get the attribute named OnlyFavorite from my current URL by reflexion. I already try to pass OnlyFavorite=null on the action link but it doesn't work: the routing says "ok, I don't have a value for OnlyFavorite on the link itself but I have OnlyFavorite on the URL so I use it!".
When you are on the Deleted page, the link is being processed that way because the StatusCode token is equal to Deleted, so the first route is satisfied. Change your link as follows:
#Html.ActionLink("Favorites", "SearchAffaires", new { StatusCode = String.Empty, OnlyFavorite = true })
UPDATED
The best solution is to reverse your routes. As a general rule, the most specific routes should always go first, and you should have more generic routes later. Since the "Affaire Only Favorite" route is more specific, it should always go first. If it is the first route satisfied, that should address your issue.
UPDATE #2
I ran a test, and all of the links were generated correctly, when I set the routes as follows:
routes.MapRoute(
"Affaire Only Favorite", // Route name
"Affaire/Favorite", // URL with parameters
new { controller = "Affaire", action = "SearchAffaires",
StatusCode = String.Empty, Page = 1, OnlyFavorite = true } // Parameter defaults
);
routes.MapRoute(
"Affaire Status Open/Closed/Deleted", // Route name
"{controller}/{StatusCode}", // URL with parameters
new { action = "SearchAffaires" }, // Parameter defaults
new { controller = "Affaire", StatusCode = "^Open$|^Closed$|^Deleted$" } // Constraints
);
In addition, the following more generic routes also generated correctly:
routes.MapRoute(
"Favorite", // Route name
"{controller}/Favorite", // URL with parameters
new { action = "Search",
StatusCode = String.Empty, Page = 1, OnlyFavorite = true } // Parameter defaults
);
routes.MapRoute(
"Status Open/Closed/Deleted", // Route name
"{controller}/{StatusCode}", // URL with parameters
new { action = "Search" }, // Parameter defaults
new { StatusCode = "^Open$|^Closed$|^Deleted$" } // Constraints
);
For the more generic routes to work, I had to rename the action as Search and I had to change each link from SearchAffaires to Search.
Well, you could use Html.RouteLink helper to point exactly to the route used. However, as #counsellorben pointed out, you should set your more specific route to be the first one.
And if there's no problem to show your url like "Affaire/Favorite/True", then you can use the example below:
routes.MapRoute(
"Affaire Only Favorite", // Route name
"{controller}/Favorite/{OnlyFavorite}", // URL with parameters
new { action = "SearchAffaires", Page = 1, OnlyFavorite = ""}, // Parameter defaults
new { controller = "Affaire" } // Contraints
);
I want to map all CMS pages url to single controller(PageController) and action(Details).
How can I create custom routing to map all these urls?
/teacher
/teacher/kindergarten
/teacher/kindergarten/1
/teacher/primary
/teacher/primary/english
/teacher/primary/language
/teacher/primary/language/chinese
/teacher/primary/math
/teacher/primary/science
/parent
/parent/kindergarten
/parent/primary1-3
/parent/primary4-6
/leader
/leader/kindergarten
/leader/kindergarten/1
If you have these URLs in a database you could map the routes when the application starts up:
var pages = siteDB.Pages.ToList();
string pagePath = "";
foreach (var page in pages)
{
routeVals = new RouteValueDictionary();
constraints = new RouteValueDictionary();
routeVals.Add("controller", "page");
routeVals.Add("action", "details");
constraints.Add("path", "[a-zA-Z0-9\\-]*");
// any child pages? must add these routes before their parent pages.
var childPages = siteDB.Pages.Where(p => p.ParentPageId == page.PageId).ToList();
foreach (var childPage in childPages)
{
pagePath = BuildPath(childPage);
RouteTable.Routes.Add(new Route(pagePath, new MvcRouteHandler())
{
Defaults = routeVals,
Constraints = constraints,
DataTokens =
new RouteValueDictionary {
{ "pageid", childPage.PageId },
{ "path", pagePath }
}
});
// Any further child pages? (Only 3 levels supported)
var childSubPages = siteDB.Pages.Where(p => p.ParentPageId == childPage.PageId).ToList();
foreach (var childSubPage in childSubPages)
{
pagePath = BuildPath(childSubPage);
RouteTable.Routes.Add(new Route(pagePath, new MvcRouteHandler())
{
Defaults = routeVals,
Constraints = constraints,
DataTokens =
new RouteValueDictionary {
{ "pageid", childSubPage.PageId },
{ "path", pagePath }
}
});
}
}
This code takes the pages from a database where they are linked by parent id.
Here's the BuildPath function which generates a full path to each page:
public static string BuildPath(Page page)
{
if (page.ParentPageId == 1)
{
return page.PageKey;
}
else
{
SiteDataEntities siteDB = new SiteDataEntities();
string path = page.PageKey;
Page parent = siteDB.Pages.Find(page.ParentPageId);
while (parent != null)
{
path = parent.PageKey + "/" + path;
parent = siteDB.Pages.Find(parent.ParentPageId);
if (parent.PageKey == "home") break;
}
return path;
}
}
Previous proposed solution is working only for small amount of pages.
Because according to the code:
application generate and register Route for each of site page. In result we have at least same amount of routes as pages in our site. As you probably know RouteModule have to check route by route each of them to find first right one and execute correct handler, controller, action, view...
There are two other way to solve this:
You can create a class that derives from RouteBase and implement the properties and methods that you need: split url to segments, determinate current page fill RouteValueDictionary with pageid, path, parents etc
You can customize UrlRewriteModule with custom rewrite provider. Idea to transform all requests url from tree base structure to mvc default route:
{controller}/{action}/{id}?path=parentlevel1/parent2/parent3/....
90% -same code for both variants could be prepared.
that solution also could be useful when you have different controllers, correct one we could determinate by current page (by page data: type)
I got a ASP.NET MVC 2.0 Preview 1 app and started to create some areas which contains their own routes.
I want to have a way to overwrite these routes in the main project. I can of course not add a new route with the same name. I can see the RouteTable.Routes.Remove(RouteBase item) but not sure how to use it.
//Need to remove "PostIndex" before adding it again
routes.MapAreaRoute(
"OurAreaNameSpace",
"PostIndex",
"post/index/{currentPage}",
new { controller = "Post", action = "Index", currentPage = "" },
new string[] { "OurAreaNameSpace.Controllers" }
);
How do in this?
RouteTable.Routes.Remove(RouteTable.Routes["PostIndex"]);