MVC Route config without fixed value? - asp.net-mvc

Is it possible the accomplish route config in MVC without fixed value? Does this method affects other routes too?
For example, i want to do;
website.com/category/my-title
website.com/other-category/another-title
website.com/another-category/and-another-title
When i try something like this;
routes.MapRoute(
name: "Content",
url: "{category}/{title}",
defaults: new { controller = "Main", action = "Content"}
);
Sometimes it works as expected but mostly it cause confusion. Some urls for default route and other static pages can not be found because mvc thinks its still category/title kinda url it is. I can fix this with some "fixed" value like
routes.MapRoute(
name: "Content",
url: "fixed/{category}/{title}",
defaults: new { controller = "Main", action = "Content"}
);
Is it possible to do this without fixed value and use another route's in mvc?

I assume you need attribute routing more than changing it in whole project:
Use this before your [HttpGet] method:
[Route("{category}/{title}")]
More information is provided here if I understood correctly your issue.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Is it possible to do this without fixed value and use another route's in mvc?
Unfortunately, if you want to have domain.com/{category}/{title} you'll have conflict with a lot of routes, especially the default route which accepts both string controller name and string action name.
That fixed value is the easiest fix to the issue and it's the only real fix, my suggestion below is somehow a work-around;
If you want to retain that route domain.com/{category}/{title}, you could add an if statement to your Content action method that checks if that category belongs to a Controller name.
public ActionResult Content(string category, string title)
{
// initialize list of controller names, better if this from a static function
List<string> controllerNames = new List<string>(){ "Home","Main","Product" };
// check if category parameter matches a controller name
var check = controllerNames.FirstOrDefault(s=>s==category);
// if it matches a controller name, redirect
if(check != null)
{
return RedirectToAction(title,category);
}
else
{
// do what Content action normally does
}
}

Related

How do I do Short URLs in MVC?

Suppose I want to publish (like in paper catalogs) some "short URLs" that are easy to type/remember, but I want them to redirect to a verbose, SEO-friendly URL. How do I accomplish that with MVC routes?
Example:
http://mysite.com/disney
becomes
http://mysite.com/travel/planning-your-disney-vacation (with "travel" as the Controller)
The things I've tried:
Just setup a route for it. Problem: the URL doesn't change in the browser (it stays "/disney".
Use NuGet package RouteMagic (see Haacked's article). Problem: I get an error: The RouteData must contain an item named 'controller' with a non-empty string value. I think this is because I don't have a static word before my controller ("travel") like he did (with "foo" and "bar")???
Use a redirect module (like Ian Mercer's). Problem: the route matches on my HTML.ActionLinks when creating URLs which I don't want (Haacked mentions this in his article and says that's why he has GetVirtualPath return NULL ...?)
I'm out of ideas, so any would be appreciated!
Thanks!
You could set up a catch-all type route, to direct all /something requests to a specific action and controller, something like:
routes.MapRoute(
"ShortUrls",
"{name}",
new {controller = "ShortUrl", action = "Index", name = UrlParameter.Optional}
);
(depending on how the rest of your routing is set up, you probably don't want to do it exactly like this as it will likely cause you some serious routing headaches - but this works here for the sake of simplicity)
Then just have your action redirect to the desired URL, based on the specified value:
public class ShortUrlController : Controller
{
//
// GET: /ShortUrl/
public ActionResult Index(string name)
{
var urls = new Dictionary<string, string>();
urls.Add("disney", "http://mysite.com/travel/planning-your-disney-vacation");
urls.Add("scuba", "http://mysite.com/travel/planning-your-scuba-vacation");
return Redirect(urls[name]);
}
}
I just faced the same problem.
In my Global:
routes.MapRoute(
"ShortUrls",
"{name}",
new { controller = "Home", action = "Index", name = UrlParameter.Optional }
);
In my Home Controller:
public ActionResult Index(string name)
{
return View(name);
}
This way is dynamic, didn't want to have to recompile every time I needed to add a new page.
To shorten a URL you should use URL rewriting technique.
Some tutorials on subject:
url-rewriting-with-urlrewriternet
url-routing-with-asp-net-4
URL rewriting in .Net

Can't get MVC Routing to work

I have a site and the url looks like this;
Home/LocationPage?locationId=f25a9ba4-54dc-4e6a-bdbf-094a5a6f7801
What I would like it to look like is;
Home/My Restaurant
I've tried mapping routes like this;
routes.MapRoute(
"Location",
"{Controller}/{LocationName}",
new { controller = "Home", action = "LocationPage", LocationName = "" }
But can't seem to get the URL to change at all.
Keep the route you just made, but you may need to add in a couple more things.
Add in a route constraint (or override the OnActionExecuting method on the controller or add a attribute to the action method) that looks up the the LocationName route parameter in the database and (if found) adds a locationId parameter to the route parameters. Then create an action called LocationPage that takes a locationId (location Name is optional).
That way you get nice urls coming in, but the action doesn't need to do the lookups.
If you receive a parameter called LocationId in your Action then you need to change your route:
routes.MapRoute(
"Location",
"{Controller}/{LocationId}",
new { controller = "Home", action = "LocationPage", LocationId = "" }
And don't forget that in this way you wouldn't get this parameter as Guid then you should make your logic to retrieve the Guid value by the provided name.

How can I dynamically add to the ASP.NET MVC RouteTable?

We've got an area on our site where people can sign up and be given their own page on the site which we want to host at ~/pageSlug. I've tried doing it with a rule in Global.asax, but that broke the fundamental default route that allows ~/Controller to map directly to the Index action. I'm not being allowed to put any kind of separator in front of the userSlug, so ~/p/pageSlug isn't really an option here.
In terms of getting the user pages added to the routes, I'm cycling through the pages at App_Start and explicitly adding them to the RoutesTable. This is working fine, and we've got AppPool refreshes set long enough to make this a once a day task. This does leave us with a 24-hour turnaround to "get pages live" for our users though, which I'm trying to solve.
Ideally, what I'd like to do is add the relevant route to the RouteTable dynamically once a user has signed up. I've tried doing that with:
RouteTable.Routes.Add(
RouteTable.Routes.MapRoute(
toAdd.UrlSlug + "Homepage",
toAdd.UrlSlug,
new { controller = "Controller", View = "View", urlSlug = toAdd.UrlSlug }
)
);
but that didn't seem to work. I can't find a solution anywhere else, and I'm sure my code is both horribly naive and betrays a lack of understanding of routing - please help!
What if you try this, using route constraint. Get out list of all users and constraint the chosen route to match entries in that list
public class UserPageConstraint : IRouteConstraint
{
public static IList<string> UserPageNames = (Container.ResolveShared<IUserService>()).GetUserPageNames();
bool _IsUserPage;
public UserPageConstraint(bool IsUserPage)
{
_IsUserPage = IsUserPage;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (_IsUserPage)
return UserPageNames.Contains(values[parameterName].ToString().ToLower());
else
return !UserPageNames.Contains(values[parameterName].ToString().ToLower());
}
}
Then in the Global.asax.cs, set up a route for users as follows:
routes.MapRoute("UserHome", "{userPage}", new { controller = "UserPageController", action = "Index" }, new { userPage = new UserPageConstraint(true) });
For this above route, in the action 'index' of the UserPageController, we will have the userPage as the parameter.
For other routes relative to the userPage Home, we can add routes accordingly. for example, the userdetails page route can be added as follows:
routes.MapRoute("UserHome", "{userPage}/mydetails", new { controller = "UserPageController", action = "Details" }, new { userPage = new UserPageConstraint(true) });
You can try this and see.
I recently just realized it too, instead of adding routes dynamically which I am not sure can be done so without using external libraries such as Route Magic
. I am confident that if you design your architecture right, Routing Constraint is all you might just need. Since my application is small, I am using only one implemented controller and dynamically creating the rest (experimental at the moment but it should work).
Consider following routes
routes.MapRoute(
name: "ContactRequests_Operations",
url: "ContactRequests-{operation}",
defaults: new { controller = "Content", action = "Module_Direct", id = "ContactRequests" , operation = "Get" }
);
routes.MapRoute(
name: "Messages",
url: "Messages",
defaults: new { controller = "Content", action = "Direct", id ="Messages" }
);
I don't think you can add route dynamically.
Take a look at this question, maybe it will help you:
ASP.NET MVC custom routing for search

How can i create a route that returns a URL without controller reference in ASP.NET MVC

I have a controller call DefaultController. Inside this controller i have views for what would be the equivalent of static pages.
The URLs look like www.site.com/Default/PageName
Is it possible to create a route that would format these URL like:
www.site.com/PageName
I want to avoid creating controllers for each of these. An alternative would be to create .aspx pages in the root but can i create routes for these pages ie:
www.site.com/PageName.aspx becomes www.site.com/PageName ?
Thanks!
You can create explicit route for the PageName action on the DefaultController like this:
routes.MapRoute(
"PageName",
"pagename",
new { controller = "DefaultController", action = "PageName" }
);
You have to put this route before the default MVC route. The biggest drawback for this approach is that you have to create one route per static page.
An alternative approach would be to add an additional route after the default MVC route:
routes.MapRoute(
"DefaultController",
"{page}/{*path}",
new { controller = "DefaultController", action = "{page}" }
);
The drawback for this approach is that this rule would be handling all the URLs, even those that would normally return 404.
First approach
Create a route that catches actions:
routes.MapRoute(
"Catcher1",
"{action}",
new { controller = "Default", action = string.Empty });
But this means you'd have to create just as many controller actions on your default controller.
Second approach
If you'd like to avoid that as well and have just one controller+action instead, write a route this way:
routes.MapRoute(
"Catcher2",
"{path}",
new { controller = "Default", action = "PageName", path = string.Emtpy },
new { path = #"[a-zA-Z0-9]+" });
This route also defines a route constraint so it will catch only those routes, that actually have something in first route segment. You can define this constraint to only catch those requests that you need (ie. path = "Result|Search|Whatever")
then your DefaultController would have something like this:
public ActionResult PageName(string path)
{
// code goes here
}
Second approach seems very feasible, but I wouldn't recommend it because all logic would have to go through this controller action (for these kind of requests). It would be better to separate these actions into logical ones. Those that actually do the same thing (so they wouldn't have a bunch of switch statements or similar) would be defined with separate routes (if they couldn't be done using a single one).

ASP.NET-MVC . How to get the controller name from an url?

How can I get the controller name of a relative Url, using the routes I have defined in Global.asax?
Example:
if I have a route defiend like this:
routes.MapRoute(
"Default", // Route name
"{language}/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "", language = "en" }
from the string "~/en/products/list" I want to have products (the controller name). Is there any existing method that already does this?
You should probably add another route like George suggests but if you really just need the controller value derived from the route you can do this in your controller action methods:
var controller = (string)RouteData.Values["controller"];
See Stephen Walther's blog post ASP.NET MVC Tip #13 – Unit Test Your Custom Routes
The project MvcFakes has an old System.Web.Abstractions reference. So you must replace it
with the new one and recomply the project to get MvcFakes.dll.
This is my code:
public string getControllerNameFromUrl()
{
RouteCollection rc = new RouteCollection();
MvcApplication.RegisterRoutes(rc);
System.Web.Routing.RouteData rd = new RouteData();
var context = new FakeHttpContext("\\" + HttpContext.Request.Url.AbsolutePath);
rd = rc.GetRouteData(context);
return rd.Values["action"].ToString();
}
In my code above "MvcApplication" is the class name in the Global.asax.
Good luck !
I'm not sure what you're asking, so if my answer's wrong, it's because I'm guessing at what you want.
You can always add another route to your Global.asax. That's often the easiest way to deal with cases 'outside of the norm'.
If you want to return a list of products, you'll use this route:
routes.MapRoute(
"ProductList",
"{language}/{products}/{action}/",
new { controller = "Products", action = "List", language = "en" });
You can also replace products with the more generic {controller} if more than one type of entity is going to use this route. You should modify it for your needs.
For example, to make this a generic route that you can use to get a list of any product:
routes.MapRoute(
"ProductList",
"{language}/{controller}/{action}/",
new { controller = "Products", action = "List", language = "en" });
What this does is that it creates a route (that you should always place before your Default route) that says, "For whatever the user enters, give me the controller and action they ask for". (Such as /en/Products/List, or /en/Users/List).
To visit that controller, you simply need to navigate to the following: yoursite.com/en/products/list. You can also use the HTMLActionLink to visit the controller.
<%=Html.ActionLink("Product", "List", new { controller = Products }, null ) %>
I'm writing this without my IDE open, so the ActionLink may have an error in it.

Resources