I need to add a http://mysite/categoryname route, so i added
routes.MapRoute(
"Categories",
"{CategoryName}",
new { controller = "News", action = "Category", CategoryName = "" },
new string[] { "MyProj.Controllers" }
);
The problem is that if i add it before
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "News", action = "Index", id = UrlParameter.Optional },
new string[] { "MyProj.Controllers" }
);
Home page goes in error because it enters in Categories route; if i add Categories route in last position it is never entered and http://mysite/category_name gives me 404.
What am i doing wrong?
You have a few options here:
Change your news category route to include a hard path:
routes.MapRoute(
"Categories",
"category/{CategoryName}",
new { controller = "News", action = "Category", CategoryName = "" },
new string[] { "MyProj.Controllers" }
);
Change your default route to include a hard path:
routes.MapRoute(
"Default", // Route name
"site/{controller}/{action}/{id}", // URL with parameters
new { controller = "News", action = "Index", id = UrlParameter.Optional },
new string[] { "MyProj.Controllers" }
);
Roll your own custom routing class
See http://hanssens.org/post/ASPNET-MVC-Subdomain-Routing.aspx for an unrelated example.
No matter how you do this, as you add Categories, you will obviously run the risk of stepping on other pages in the site - so for example if you have a section of your site named /account and then someone creates a category named, "account," what's supposed to happen?
That said, there is a simpler answer than writing your own Routing class. You can use a Regex in the Category Route, and make it the first rule. For example, if the only 2 categories on the site were jackets and sweaters:
routes.MapRoute(
"Categories",
"{CategoryName}",
new { controller = "News", action = "Category" },
new { CategoryName = "(jackets|sweaters)" }
);
The final argument is a RouteConstraint based in Regex, and so the route will defer to routes included after it if the path is neither /jackets or /sweaters.
Obviously you want to be more robust than that, so you can create a method that builds the Regex at app startup:
routes.MapRoute(
"Categories",
"{CategoryName}",
new { controller = "News", action = "Category" },
new { CategoryName = "(" + String.Join("|", categories) + ")" }
);
categories here would need to be something you provide - some array or database feed of the category names in your app.
Related
I need to implement SO like functionality on my asp.net MVC site.
For example when user go to https://stackoverflow.com/questions/xxxxxxxx
after loading the subject line is concatenated with the url and url becomes like this https://stackoverflow.com/questions/xxxxxxxx/rails-sql-search-through-has-one-relationship
Above "/rails-sql-search-through-has-one-relationship " part is added to the url.
In webforms it's simple, I could just use url rewriting. But not sure how to accomplish this in MVC
The following line is from Global.asax file
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Account", action = "LogOn", id = UrlParameter.Optional } // Parameter defaults
);
the string that I need to concatenate is in my database so it fetches from there. How can I accomplish this?
This is called a slug route. One way to achieve this is to define a route with an optional slug parameter, and in the controller method check if the parameter has been provided
routes.MapRoute(
name: "Question",
url: "Question/{id}/{slug}",
defaults: new { controller = "Question", action = "Details", slug = UrlParameter.Optional }
);
Then in QuestionController (assumes an id will always be provided)
public ActionResult Details (int id, string slug)
{
if (string.IsNullOrEmpty(slug))
{
// Look up the slug in the database based on the id, but for testing
slug = "this-is-a-slug";
return RedirectToAction("Details", new { id = id, slug = slug });
}
var model = db.Questions.Find(id);
return View(model);
}
You are looking for a custom route. If you look closely, SO doesn't care about the text part of the URL. So:
http://stackoverflow.com/questions/xxxxxxxx/rails-sql-search-through-has-one-relationship
AND
http://stackoverflow.com/questions/xxxxxxxx/
Will both work. You can easily do that with something like:
routes.MapRoute(
"Question",
"questions/{id}/{title}",
new { controller = "Question", action = "Details" });
The trick is add the "slug" at the end when you create links:
#Html.RouteLink(
"Read more.",
"Question",
new { id = question.Id, title = Slugger.ToUrl(question.Title) })
I want users to be able to access the "/Linecard" page of my ASP.Net MVC site using "/Linecard" or "/Manufacturers" as the URL... so same controller, 2 different possible URLs.
I tried adding the following:
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
Adding this after the "Default" route doesn't work at all and I get a 404 error when I go to "/Manufacturers". Putting it BEFORE "Default" works, but then only "/Manufacturers" shows up in the URL when I click menu links since it is the first match. I would like "/Linecard" to always show as the URL.
Any pointers? Is there a certain constraint I can use to accomplish this? Thanks!
I had the same problem when we moved to extension-less URLs. We needed to continue to support one route with extensions. I got around it by having my default route apply to everything except the old URL, then after that mapping one specifically for the exception
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
// if controller specified does not match 'manufacturers' (case insensitive)
new { controller = "^((?i)(?!manufacturers).)*$" },
new string[] { "Namespace.Of.Controllers" }
);
routes.MapRoute(
"Manufacturers", // Route name
"Manufacturers/{action}/{id}", // URL with parameters
new { controller = "Linecard", action = "Index", id = UrlParameter.Optional },
new string[] { "Namespace.Of.Controllers" }
);
You could also set an order when mapping your routes with the defaults at the end like so
routes.MapRoute(
name: "Manufacturers",
url: "Manufacturers/{action}/{id}",
defaults: new { controller = "Linecard", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
ActionLink result "http://localhost:5089/Article/GetArticlesByCategory?category=ASP.NET&categoryId=2". i want to show that link type "http://localhost:5089/Blog/ASP.NET". what is wrong route named "Article".
Routes:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Article",
"Blog/{category}", //
new { controller = "Article", action = "GetArticlesByCategory", category = UrlParameter.Optional, categoryId = UrlParameter.Optional }
Link:
#Html.ActionLink(k.Name, "GetArticlesByCategory", "Article",
new { category = k.Name, categoryId = k.CategoryId }, null)
SOLVED
GetArticlesByCategory parameter int categoryId changed to >> string category and replaced action codes as to new parameter (string category)
Routes replaced with:
routes.MapRoute(
"Category",
"Blog/{category}",
new { controller = "Article", action = "GetArticlesByCategory", category = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "index", id = UrlParameter.Optional } // Parameter defaults
);
ActionLink replaced with:
#Html.ActionLink(k.Name, "GetArticlesByCategory", "Article",
new { category = k.Name }, null)
There are a few issues. First, and most important, your routes are specified in the wrong order. The default route should be defined last. Second, never define a route with two optional parameters. It just causes too many problems.
Try the following for your routes:
routes.MapRoute(
"CategoryAndId",
"Blog/{category}/{categoryId}",
new { controller = "Article", action = "GetArticlesByCategory" }
);
routes.MapRoute(
"CategoryOnly",
"Blog/{category}",
new { controller = "Article", action = "GetArticlesByCategory",
category = UrlParameter.Optional }
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "index",
id = UrlParameter.Optional } // Parameter defaults
);
You are not specifying the action in the route
routes.MapRoute(
"Article",
"Blog/{action}/{category}/{categoryId}", //
new { controller = "Article", action = "GetArticlesByCategory", category = UrlParameter.Optional, categoryId = UrlParameter.Optional }
I suggest you use Phil Haack's routes debug, http://haacked.com/archive/2008/03/13/url-routing-debugger.aspx. A great way for debugging your MVC routes
If you want the link to show as http://localhost:5089/Blog/ASP.NET, you'll need to change the actionlink as such:
#Html.ActionLink(k.Name, "GetArticlesByCategory", "Article",
new { category = k.Name }, new { #title = "Kategorisindeki Makaleler", #class = "selected" })
Since you don't want the CategoryID in the link, there is no need to put it in. the route isn't being matched by the actionlink because it expects a CategoryID parameter as well
EDIT
If you want the CategoryID to be read from the route, it needs to be added to the route. otherwise it will just be appended as a parameter (like in your original example).
If you change your route to:
"Blog/{categoryId}/{category}"
or
"Blog/{category}/{categoryId}"
The link will now look like Blog/2/ASP.NET or Blog/ASP.NET/2 but if you want the categoryId to be read from the URL, then I don't think you have much choice
I am new to ASP.Net MVC. May be this question looks simple, but i couldn't fix it. Here the scenario. I have an application listing data based on city. So the url will be looking like this
www.xxxxxx.in/chennai
www.xxxxxx.in/mumbai
www.xxxxxx.in/delhi
In normal routing the first part (chennai/mumbai) is controller in the above url, But here i dont want this to be a controller. instead i want to map the single controller (LocationController) to these URl's. Because later time i can add any number of city.
I am struck here, can someone help me out.
Try this:
routes.MapRoute(
"CityRoute", // Route name
"{city}", // URL with parameters
new { controller = "Location", action = "Index", city = "" } // Parameter defaults
);
I am not sure there won't be easier option than this, but you can try this - using route constraint. Basically, you need to know the list of cities you have and then constrain the route to match only entries in that list.
The route constraint can be implemented as follows
public class CityConstraint : IRouteConstraint
{
public static IList<string> CityNames = (Container.ResolveShared<ICityService>()).GetCities();
bool _IsCity;
public CityConstraint(bool IsCity)
{
_IsCity = IsCity;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
if (_IsCity)
return CityNames.Contains(values[parameterName].ToString().ToLower());
else
return !CityNames.Contains(values[parameterName].ToString().ToLower());
}
}
And then put the route as follows:
routes.MapRoute("Location", "{cityName}", new { controller = "LocationController", action = "Index" }, new { cityName = new CityConstraint(true) });
Also make sure the above route is listed before the default route
routes.MapRoute("Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional
);
Also note that, no controller name can be a city name.
Try this and see.
If all your routing is related to these cities than remove default route and replace it with this route definition:
routes.MapRoute(
"Default",
"{city}",
new { controller = "Location", action = "Index", city = "Mumbai" }
);
Then create a LocationController class:
public class LocationController : Controller
{
public ActionResult Index(string city)
{
// do whatever needed; "city" param has the city specified in URL route
}
}
If you still need your default route (controller/action/id) for other pages not just cities then it's probably better to put a constraint on your default route and define them like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = "Home|...|..." } // put all controllers here except "Location"
);
routes.MapRoute(
"Location",
"{city}",
new { controller = "Location", action = "Index", city = "Mumbai" }
);
This will make other controllers still working and location will work just as well. The problem is of course if there's a city name that's the same as a name of one of your regular controllers. :) But you can control/avoid that as well.
You can do that by adding a route that hardcodes the controller name:
routes.MapRoute(
"location", // Route name
"{cityName}", // URL with parameters
new { controller = "location", action = "index" } // Parameter defaults
);
routes.MapRoute(
"Location", // Route name
"{controller}/{action}/{cityName}", // URL with parameters
new { controller = "Location", action = "index"} // Parameter defaults
)
This will route all requests of the form "/mumbai" to LocationController action method Index with parameter cityName set to "mumbai". It will also be able to route full controller/action spec using the second route.
I've got two (so far) different types of routes in my ASP.NET MVC app, one is: {controller}/{action}/{id} and the other {controller}/{action}/{title}
Currently I need to define the routes like this:
routes.MapRoute (
"Default_Title_Slug", // Route name
"product/details/{title}", // URL with parameters
new { controller = "product", action = "details", title = "" } // Parameter defaults
);
routes.MapRoute (
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "site", action = "index", id = "" } // Parameter defaults
);
Notice that the first one I've had to tie down to the product controller, this seems to be the only way I can get it work...otherwise the other routes end up looking like this:
/controller/action?id=number
Now I need to add another MapRoute call targeting another controller with the {title} segment...I don't want to create a new route for each specific entry I come up with in the future...is there a generic route I can create to map the /controller/action/title that'll play nicely with the /controller/action/id route?
Thanks,
Kieron
You can do that with a route-constraint, such as regex - a very similar example is here. Something like:
routes.MapRoute (
"Default",
"{controller}/{action}/{id}",
new { controller = "site", action = "index", id = "" },
new { id = #"\d+" }
);
routes.MapRoute (
"Default_Title_Slug",
"{controller}/{action}/{title}",
new { controller = "product", action = "details", title = "" }
);