Given the following routes:
routes.MapRoute(name: "CityHomePage1", url: "{city}-{state}", defaults: new { controller = "Home", action = "GeoHomePage" });
routes.MapRoute(name: "CityHomePage2", url: "{city}-{state}/", defaults: new { controller = "Home", action = "GeoHomePage" });
routes.MapRoute(name: "CityStateResults", url: "{city}-{state}/{searchTerm}", defaults: new { controller = "Results", action = "SearchCityState" });
routes.MapRoute(name: "CityStateCategoryResults", url: "{city}-{state}/{category}/{searchTerm}", defaults: new { controller = "Results", action = "SearchCityStateCategory" });
This works well when the cities do not have dashes ("-") in them, however I am now changing the way multiple cities are displayed in the URL from having an underscore between them, to now having dashes.
So, if the URL reads http://www.site.com/Gardena-Ceretos-Santa_Monica-California/someterm it the routes will no longer work.
How would I grab the state as everything after the last "-" and before the "/" in my routes to extract the state?
Looking at this now, I should have structured the URLS as /city-city-city/state/searchtearm, but for now, I have to stick with the current structure.
Thanks.
This is a situation where routing does not have any tools to assist you. There are two options.
First, change your token to {cityState}, make that the parameter in your action methods, and parse the cityState string in your controller actions.
Second, change your token to {cityState} (I promise, this is a different option), and then use a custom RouteHandler to parse the cityState string, and add city and state route tokens to pass to your existing actions. The RouteHandler would look like so:
using System.Web.Mvc;
using System.Web.Routing;
public class MyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var routeData = requestContext.RouteData;
var cityState = routeData.Values["cityState"].ToString();
var parts = cityState.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries);
var state = parts.Last();
var citySb = new StringBuilder();
foreach (var part in parts)
{
if (part != state)
{
if (citySb.Length > 0)
citySb.Append("-");
citySb.Append(part);
}
}
routeData.Values.Add("city", citySb.ToString());
routeData.Values.Add("state", state);
var handler = new MvcHandler(requestContext);
return handler;
}
}
Then, change each of your routes to be similar to this:
routes.MapRoute(
"CityHomePage1",
new Route(
"{cityState}",
new RouteValueDictionary(
new { controller = "Home", action = "GeoHomePage" }),
new MyRouteHandler()
)
)
);
Related
I have some links in my webapp that looks like this:
localhost:12345/?something=1
localhost:12345/?something=2
localhost:12345/?something=3
localhost:12345/?something=4
each number at the end is an id that i need to pass to my controller to display information related to it.
I know I need to create a new routes.MapRoute in my global.asax page, but I am not really quite sure how to go about it. I tried this:
routes.MapRoute(
"Id", // Route name
"{controller}/{action}/{*Id}", // URL with parameters
new { controller = "Home", action = "Id", Id = "" } // Parameter defaults
);
---EDIT---
I am only successful getting each individual like to display by doing the following:
routes.MapRoute(
"IdRoute", // Route name
"{Id}", // URL with parameters
new { controller = "Home", action = "Index", id = 1 } // Parameter defaults
);
This does work, however, this only works for one id (specifically 1). I am not quite sure how to go about this, but i need i need:
localhost:12345/?something=1
to display the information for id 1,
localhost:12345/?something=2
to display the information for id 2,
localhost:12345/?something=3
to display the information for id 3.
I there are going to be hundreds of ids so hard coding something in would not be a convenient option. I have had no luck so far. Any help would be much appreciated! Thanks!
routes.MapRouteWithName(
"RootName",
"{id}",
new { controller = "Home", action = "Index", id = 1 });
This will produce links like this localhost/1
If you want this kind of links localhost/?id= 1
Then :
routes.MapRouteWithName(
"RootName",
String.Empty,
new { controller = "Home", action = "Index"});
public ActionResult Index(int id)
{
//do something with id, make query to database whatever
// u usually have model class so you would fill model with your data
var model = new YourModel();
//...
return View("Index", model);
}
If you have following Action in, say, HomeController:
public ActionResult SomeAction(int Id)
{
return View()
}
You may use any of following routes:
//* For Id = 3 this will return path "Home/SomeAction/3"
routes.MapRoute(
name: "First",
url: "{controller}/{action}/{Id}",
defaults: new { controller = "Home", action = "SomeAction", Id= UrlParameter.Optional}
);
//* For Id = 3 this will return path "SomeAction/3"
routes.MapRoute(
name: "First",
url: "{action}/{Id}",
defaults: new { controller = "Home", action = "SomeAction", Id= UrlParameter.Optional}
);
//* For Id = 3 this will return path "Home/SomeAction(3)"
routes.MapRoute(
name: "First",
url: "{controller}/{action}({Id})",
defaults: new { controller = "Home", action = "SomeAction", Id= UrlParameter.Optional}
);
//* For Id = 3 this will return path "LadyGaga/SomeAction/3"
routes.MapRoute(
name: "First",
url: "LadyGaga/{action}/{Id}",
defaults: new { controller = "Home", action = "SomeAction", Id= UrlParameter.Optional}
);
I am teaching myself asp .net mvc3 by creating a blog application. However, I have
problems with comment upload. It is a very subtle error in that everything works when a user leaves a comment. However, the url of the post changes.
So, a blog post has a url
http://localhost:49175/Blog/Details/3/Third-post
This is generated by the url route map here:
routes.MapRoute(
"BlogDetail", // Route name
"Blog/Details/{id}/{urlHeader}", // URL with parameters
new { controller = "Blog", action = "Details", id = UrlParameter.Optional, urlHeader = UrlParameter.Optional } // Parameter defaults
);
Now, when a user leaves a comment - he is directed to a comment controller:
[HttpPost]
public ActionResult Create(BlogDetailsViewModels viewModel)
{
if (ModelState.IsValid)
{
try
{
blogrepository.Add(viewModel.Comment);
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID });
}
catch (DataException)
{
ModelState.AddModelError("", "Unable to save comment. Try again, and if the problem persits then contact administrator.");
}
}
// If we got this far, something failed, redisplay form
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID });
}
}
However, when somebody leaves a comment - he is redirected back to
http://localhost:49175/Blog/Details/3
I know, as of now there is nothing in the RedirectToAction that passes the urlHeader info. However, I have tried a few things like:
return RedirectToAction("Details", "Blog", new { id = viewModel.Comment.BlogID, urlHeader = viewModel.Blog.UrlHeader });
However, it doesn´t seem to work.
This is the blog details controller:
//
// GET: /Blog/Details/5
public ViewResult Details(int id, string urlHeader)
{
var blogs = blogrepository.GetBlog(id);
var recentblogs = blogrepository.FindRecentBlogs(5);
var archivelist = blogrepository.ArchiveList();
BlogDetailsViewModels viewModel = new BlogDetailsViewModels { Blog = blogs, RecentBlogs = recentblogs, ArchiveList = archivelist };
return View(viewModel);
}
I am stuck for days on this.
-- Full route method as requested --
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"BlogDetail", // Route name
"Blog/Details/{id}/{urlHeader}", // URL with parameters
new { controller = "Blog", action = "Details", id = UrlParameter.Optional, urlHeader = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"BlogArchive", // Route name
"Blog/{year}/{month}", // URL with parameters
new { controller = "Blog", action = "Archive" }, // Parameter defaults
new { year = #"\d{4}", month = #"\d{1,2}", } // Parameter constraints
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
If your form does not contains data for viewModel.Blog.UrlHeader, it will be an empty string, even viewModel.Blog may be null.
You can add a parameter to your post action method, like this:
[HttpPost]
public ActionResult Create(BlogDetailsViewModels viewModel, String urlHeader)
And, in your view that renders the form, use this code to generate the form element:
#Html.BeginForm("Create","Blog",new{urlHeader=Model.Blog.UrlHeader})
Alternatively, you can add a hidden input in your form for the urlHeader. In this way, you don't have to do any of previous two updates.
#Html.HiddenFor(m=>m.Blog.UrlHeader)
Either way, make sure your Model.Blog.UrlHeader is not null or an empty string
I am trying to implement some routing for a blog I want the url to be displayed with mutiple parameters but It I keep getting loads of 500 network errors and it seems that it starts looking for jquery and images in different places when i get the desired route I want.
Heres the route
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Events",
"Post/{action}/{id}/{categoryName}/{blogTitle}",
new { controller = "Post", action = "Detail", id = "",
categoryName = "", blogTitle = "" }
);
heres the controller
public ActionResult Details(int id, string categoryName, string blogTitle)
{
SitePosts posts = new SitePosts();
Post post = posts.GetPost(id);
ViewBag.IsAdmin = IsAdmin;
return View(post);
}
and here is the link I am using in a cshtml razor helper
<a href="#Href("~/Post/Details/" + post.ID + "/" + post.Category.CategoryName + "/" + post.Title)">
same thing applies if i do a redirect to action
return RedirectToAction("Details", "Post", new { id = uid, categoryName = post.Category.CategoryName, blogTitle = UrlEncoder.ToFriendlyUrl(post.Title) });
please I have no idea why this is happening
I have a screen print of lots of http get errors regarding trying to find javascript and images
at post/details/content/jquery etc etc but It will not let me post
Try using the Html ActionLink helper instead of creating it yourself. Also, you can adjust your routing table and omit the values that you are passing, you do not need to declare those in your route with the = "" syntax, just leave them in the route expression.
Route Table
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Events",
"Post/Detail/{id}/{categoryName}/{blogTitle}",
new { controller = "Post", action = "Detail" }
);
View - Create a Link
#Html.ActionLink("DisplayText", "Detail", new{controller="Post", id = post.ID,
categoryName=post.Category.CategoryName, blogTitle = post.Title})
new to asp.net mvc (using v3 + razor) and am wondering how to best solve a problem with creating dynamic routes based on a database. Essentially, the main site navigation will be entered into a database and I want to load them up as routes. i.e. - Load Category list from database, then append the routes to the routing engine if possible...
mysite.com/cars
mysite.com/televisions
mysite.com/computers
etc....
Each category after the slash comes from the db, but, there are regular entries like /about and /contactus that will not be in the database and have been statically entered in the global.asax... my question is:
For the dynamic database URLs should I use a custom RouteHandler or pehaps create a ControllerFactory that will match and handle the requests for the entries loaded from the database. Is it possible to have the DefaultControllerFactory handle the routing if my RouteHandler or CustomControllerFactory don't find the route in the list from the database? Thanks for any help, very first project with this so I'm not sure what the best route is ;) no pun intended...
Update:
Tried using a route constraint that pulls from the database but it conflicts with the default route now... here is my custom constraint and routes:
public class CategoryListConstraint : IRouteConstraint
{
public CategoryListConstraint()
{
var repo = new Repository<Topic>();
var cats = repo.All();
var values = new List<string>();
foreach (var c in cats)
{
values.Add(c.URI.Replace("/", "").Replace("?", ""));
}
this._values = values.ToArray<string>();
}
private string[] _values;
public bool Match(HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection)
{
// Get the value called "parameterName" from the
// RouteValueDictionary called "value"
string value = values[parameterName].ToString();
// Return true is the list of allowed values contains
// this value.
return _values.Contains(value);
}
}
and here are the routes:
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Categories",
"{category}/{*values}",
new { controller = "Category", action = "List" },
new CategoryListConstraint()
);
Routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
The home page www.mysite.com loads using the Default route. All the URLs that match the constraint list are loaded by the category route... but if I have the www.mysite.com/admin or www.mysite.com/aboutus these are getting picked up by the Categories route even though the values are not in the constraint list. Confused...
What about something like this?
Categories controller:
public ActionResult List(string category)
{
var products = _repo.Get(category); // however you are getting your data
return View(products);
}
Routes
routers.MapRoute(
"About",
"About",
new { controller = "Home", action = "About" });
//... other static routes
routes.MapRoute(
"CategoriesList",
"{id}",
new { controller = "Categories", action = "List" },
new { id = #"\w+" });
The incoming URL is tested against each Route rule to see if it matches - and if a Route rule matches then that rule (and its associated RouteHandler) is the one that is used to process the request (and all subsequent rules are ignored). This means that you want to typically structure your routing Rules in a "most specific to least specific" order
source
Found the exact solution I was looking for. Code is below. I managed to avoid using Controller Factories or implementing a custom IRouteHandler by using extending the RouteBase class which worked perfectly and allows me to pass control down to the default mvc route is something specific isn't hit. BTW - constraints ended up not working properly as the broke the controllers associated with the default route (although the default route was getting hit)
public class CustomRoutingEngine : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
var routeHandler = new MvcRouteHandler();
var currentRoute = new Route("{controller}/{*URI}", routeHandler);
var routeData = new RouteData(currentRoute, routeHandler);
// implement caching here
var list = GetConstraintList();
// set your values dynamically here
routeData.Values["controller"] = "Category";
// or
routeData.Values.Add("action", "List");
// return the route, or null to have it passed to the next routing engine in the list
var url = Util.StripSlashOnFrontAndBack(httpContext.Request.Path.ToLower()).Split('/')[0];
if (list.Contains(url))
return routeData;
return null; // have another route handle the routing
}
protected List<string> GetConstraintList()
{
using (var repo = new RavenRepository<Topic>())
{
var tops = repo.Query().Where(x => x.Hidden == false).ToList()
.Select(x=>x.Name.ToLower());
List<string> list = new List<string>();
list.AddRange(tops);
repo.Dispose();
return list ?? new List<string>();
}
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
//implement this to return url's for routes, or null to just pass it on
return null;
}
}
Then my register routes method looks like so:
Routes.Clear();
// Set Defaults
Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
AreaRegistration.RegisterAllAreas();
routes.Add(new App.Helpers.CustomRoutingEngine());
Routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Asp.Net MVC 3, when creating outgoing link for example with UrlHelper will use RouteData from current request. I dont really understand why.
Here is my routing
routes.MapRoute("car-location", "{car}/{location}/search",
new {
controller = MVC.Home.Name,
action = MVC.Home.ActionNames.Search
},
new {
car = "[a-zA-Z0-9_]+",
location = "[a-zA-Z0-9_]+"
});
routes.MapRoute("car-only", "{car}/search-car",
new
{
controller = MVC.Home.Name,
action = MVC.Home.ActionNames.Search
},
new
{
car = "[a-zA-Z0-9_]+"
});
Ok, now I try to generate links:
#Url.RouteUrl(new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Search, car = "SUV" })
#Url.RouteUrl(new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Search, location = "NY", car = "SUV" })
The result is correct when current URL is /SUV/search-car
/SUV/search-car
/SUV/NY/search
and when current url is /SUV/NY/search they both turn into
/SUV/NY/search
/SUV/NY/search
So into the first link {location} is transfered from current request. I dont want my links to be changing :)
I tried putting empty location
$#Url.RouteUrl(new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Search, car = "SUV", location = "" })
so it generates this (uses correct route but adds this as parameter)
/SUV/search-car?location=NY
Howto generate links that point to the same action and have different set of routing data and be independent of current request url.
Specify the Route name, car-location, or car-only as the first argument of the RouteUrl method like so
#Url.RouteUrl("car-only", new { controller = MVC.Home.Name, action = MVC.Home.ActionNames.Search, car = "SUV", location = "" })