Working on a MVC Application that basically is a front end database of my City's listings/Directory.
Registered Listings are called like the following:
something.com/Listings/View/{some-guid}
Is it possible to display in this format:
something.com/{slug version of the destination name}
or
something.com/kfc-arabia
This would be of great ease to share the links with clients, also SEO Friendly.
I think it would be difficult to match your slug at the root of your application as this would not allow for other routes in your application. However if you can achieve something like what you want by doing the following
something.com/directory/{slug}
With this you would just need a simply rule to match this to a suitable action that takes your slug as a parameter.
e.g.
context.MapRoute("Listings_by_slug",
"directory/{slug}",
new { controller = "Listings",
action = "ViewBySlug" });
and your action would be something like this
public ActionResult ViewBySlug(string slug){
var listingGuid = _service.GetGuidFromSlug(slug);
return RedirectToAction("View", "Listing", new { id = listingGuid);
}
UPDATE
If you really want that then you could have a route like
context.MapRoute("Listings_by_slug",
"{slug}",
new { controller = "Listings",
action = "ViewBySlug" });
You'd want to put this after other routes you need as this route matches anything to your application. In effect it should be your last declared route. Also you could not have a controlller with the same name as a slug
Related
Based on my SEO team's recommendation i am trying to generate SEO friendly urls. For some static pages i have done that easily using RouteCollection.MapRoute() like -
//Home/Solutions
routes.MapRoute("Solutions", "Solutions", new { controller = "Home", action = "Solutions" }, new[] { "MyAuction.Controllers" });
//Home/SolutionOfferings
routes.MapRoute("Offerings", "Offerings", new { controller = "Home", action = "SolutionOfferings" }, new[] { "MyAuction.Controllers" });
//Home/Pricing
routes.MapRoute("Pricing", "Pricing", new { controller = "Home", action = "Pricing" }, new[] { "MyAuction.Controllers" });
I was then trying to generate SEO friendly routes for my dynamic routes. For example there are several auctions scheduled for a day which contains hundreds of vehicles scheduled within the auction. To show details of that scheduled vehicle within the auction the actual URL is somewhat -
http://example.com/Auction/VehicleDetails?AuctionId=42&VehicleId=101
Please note that VehicleId represents the Identity within AuctionVehicles table which also contains other details of the vehicle like Make, Model, Year and VIN etc.
What i want to achieve is to generate a dynamic URL like -
http://example.com/42/honda-civic-2010-123456
where 42 is the auction id while honda is the make, civic is the model, 2010 is the year and 123456 is the last 6 digits of the VIN number.
Not sure how to achieve this.
I tried using this link -
Dynamic Routes from database for ASP.NET MVC CMS
Any help would be greatly appreciated.
Routing is one of the most difficult things to grasp in mvc. The best way i have found is MVC attribute routings in ASP.NET MVC 5. (P.s. i'm typing on a phone)
you simply include this line in your RouteConfig
routes.MapMvcAttributeRoutes();
And then you can set optional parameters and default values and map urls in your actual controllers like this:
[Route("books/{bookName?}")]
public ActionResult View(string bookName)
{
if (!String.IsNullOrEmpty(bookName)
{
return View("OneBooks"), GetBooks(bookName));
}
return View("AllBooks"), GetBooks());
}
Your url will look like www.example.com/books/jungle-book
there are many more things you can do. Please read the following article:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
I also found this links and the sublinks on this page to be helpfull to get a proper understanding of mvc routing (lots of reading!!):
https://www.codeproject.com/Articles/641783/Customizing-Routes-in-ASP-NET-MVC
As I said I think attribute routing is your best bet!
I have a Controller with the name "Hem" and Action name is "Om".
And default language i have set Swedish.
So route will be on Swedish site, it's
/sv/Hem/Om
Now I want to change language to "en" by clicking English in language section.
So route will be set automatically like this way :
/en/Home/About
But functionality should be work of /sv/Hem/Om and In address bar should be display as /en/Home/About
Experts can you please help me out.
You can do this way.
routes.MapRoute(
"English route",
"en/{controller}/{action}/{id}"
new { controller = "Home", action = "Index", language = "en" },
);
routes.MapRoute(
"FrenchHome",
"/sv/Hem/Om",
new { controller = "Home", action = "Index", language = "fr" }
);
or you can do that way:
public class GenericRoutes
{
public string Controller {get;set;}
public string Action {get;set;}
public string Url{get;set;}
public string RouteName{get;set;}
}
public List<GenericRoutes> Routes = new List<GenericRoutes>();
Routes.Add(new GenericRoutes{Cotroller="bl",Action="cl",Url="bl/cl"})
for(int i=0;i<Routes.count();i++)
{
routes.MapRoute(
Routes[i].RouteName,
Routes[i].Url,
new { controller = Routes[i].Controller, action = Routes[i].Action },
);
}
I personally would avoid this approach for multilingual sites. Yes it is technically possible to do what you are asking but most sites do not handle multiple languages in this way. ASP.net has had ability to localize pages for a long time and I would recommend this approach instead.
Localization involves putting resource keys in your view template instead of hard coding your strings. Then you would set the culture of your thread, usually by the http accept-language header and the site would chose the appropriate strings for that culture to put into the page view. The only thing you need to maintain then is sets of strings for each language.
The benefit for this approach is that you write your views only once. When you have 2 or even 3 sets of views you run the risk of having those versions of your site diverge. I personally have seen this happen and its a hard problem to get back from. Also you get to separate your "language problem" from whatever problem your website is solving, meaning your domain isn't cluttered with boilerplate code to maintain a fancy language switching technique, instead of going with the solution that's included with the platform.
If you are interested in doing multi-language sites in .net the right way I would recommend learning about Localization and Globalization, here is a good place to start :)
Beginners Tutorial
Scott Hanselman tutorial - MVC 3 + Jquery version
First of all, I am not an expert. But in order to route user to different controller you can implement custom routing and configure routes dynamically.
To understand how routing works you may consider checking this link out.
Here is a quick trick to do that:
Here is RegisterRoutes method which register all routes for application
public static void RegisterRoutes(RouteCollection routes)
{}
now get current language from url(get first segment of url (en in your case))
query your data source for current language
add your routes here from database or any other source using foreach.
.
foreach (var route in RouteValues)
{
route.UniqueName,
routes.MapRoute("prefix/{controller}/{action}/{id},
new { controller = route.Controller , action = route.Action , id = route.Id });
}
I have an architecture where I have numerous objects I can configure. Examples of URLs I want are as follows:
/configuration/building/add
/configuration/building/edit/1
/configuration/group/add
/configuration/group/edit/1
I have a Configuration controller but how do I intercept or deal with building/add and building/edit/1 etc... If it were AddBuilding I could simply add an AddBuilding() function, and similarily how do I get it to work for configuration/building/edit/
Here's what you can do for the first one - open up the Global.asax.cs file of your site and put this in RegisterRoutes before the standard MVC catch-all route (the one that uses the route "{controller}/{action}/{id}"):
routes.MapRoute("AddBuilding", "configuration/building/add",
new { controller = "Configuration", action = "AddBuilding" });
The others will be the same, but different names (first parameter) and action, whislt the edit routes but would include an {id} route placeholder and route parameter (but not optional - unlike the MVC default route):
routes.MapRoute("EditBuilding", "configuration/building/edit/{id}",
new { controller = "Configuration", action = "EditBuilding" });
By leaving the id off the route defaults we make it required. I'm assuming this, because I'm guessing the Url /Building/Edit doesn't logically map to anything.
As a side node - including verbs in your urls isn't really in keeping with REST methodology, however you're not the first to do it by a long way (I include myself in that too). That said - trying to keep to it usually makes your life a lot easier, as you'll find your controllers will be cleaner, as will your route table, and your site's URL space will be a lot smaller and more obviously hierarchical. This last point is - handy for zooming around the site at dev time, but more importantly it's crucial for SEO.
So (I've commented this code heavily, hopefully enough to provide some nuggets of knowledge!):
public class ConfigurationController{
////HTTP GET /Buildings
/// DISPLAYS BUILDINGS
public ActionResult Buildings(){
//get model and return view that shows all buildings with perhaps a
//partial in that for creating a new one (or you can use another action)
//The HTML form on that view will POST to the URL handled by the method below.
}
////HTTP POST /Buildings
/// CREATES A NEW BUILDING
//use ActionName here to make this and the method above accessible through
//the same URL
[ActionName("Buildings")]
[HttpPost]
public ActionResult CreateBuilding(BuildingModel model){
//validate the model, create the object and return the same
//view as the Buildings() method above (after re-loading all the
//buildings. Or, you can issue a redirect, effectively transferring
//control back to the method above.
}
////HTTP GET /Configuration/Building/id
///DISPLAYS A BUILDING
public ActionResult Building(int id){
//get building and return view, which also contains Edit functionality
}
////HTTP POST /Configuration/Building/id
///EDITS A BUILDING
[HttpPost]
public ActionResult Building(int id, BuildingModel model){
//very similar to the CreateBuilding method - and again you might
//either simply return a building view at the end, or redirect
//to the method above.
//Note that we don't need [ActionName] here because this and the
//get method can have the same method names, because they are overloads
//i.e. since they have different method signatures we can call them the same
//thing in code.
}
}
I've left off the group stuff to keep it short, and hopefully you'll be able to see how to do it from there.
With this in place, we only need at most two routes in Global.asax.cs - although I think the order will be important:
//handles both GET and POST to this URL, i.e. our view & edit operations
routes.MapRoute("IndividualBuilding", "/configuration/buildings/{id}",
new { controller = "Configuration", action = "Building" });
routes.MapRoute("Buildings", "/configuration/buildings",
new { controller = "Configuration", action = "Buildings" });
Now we are using the HTTP verbs to signify what we intend to do with a particular request, and our URLs have become more 'logical'.
Another refactor
If you want to be 'clever' you can lump both buildings and groups under two routes
//handles both GET and POST to this URL, i.e. our view & edit operations
routes.MapRoute("Individual", "/configuration/{controller}/{id}",
new { controller = "Configuration", action = "List" });
//again, handles GET and POST
routes.MapRoute("Collection", "/configuration/{controller}",
new { controller = "Configuration", action = "Single" });
Now you do both buildings and groups controllers as I showed above, but replace Buildings (remembering the ActionName attribute on the second method) with List and Building with Single.
One final thing to consider is that because of the default MVC route:
routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Default", action="Home", id = UrlParameter.Optional });
Both of your two controllers can still be routed via /Buildings/Single/1 or /Groups for example. This is a minor issue (dupe content isn't great SEO) but it can be something that people can use to sniff your site.
If you absolutely want to prevent this other url format; you can take out the default route, meaning you'd have to explicitly route other stuff that might already work (not a great issue).
Or you can use a little trick that will make it far harder: use explicit [ActionName] attributes with characters in the route name that won't be allowed through IIS - e.g. ":Single" or ":List", and then adjust our two routes from a couple of code blocks back accordingly.
So firstly you can create a controller action called AddBuilding() as you have hinted.
Then in your Global.asax file in the RegisterRoutes method you can add a route like so:
routes.MapRoute(
"AddBuilding", // Route name
"configuration/building/add", // URL with parameters
new { controller = "Configuration", action = "AddBuilding" }
);
You should not though that you will likely still be able to access the page using "/configuration/addbuilding" because of your default route mapping.
You edit one will be similar expect you will want to map the ID value for this:
routes.MapRoute(
"EditBuilding", // Route name
"configuration/building/edit/{id}", // URL with parameters
new { controller = "Configuration", action = "AddBuilding", id = UrlParameter.Optional }
);
I think you will need to add this code with the default MapRoute setup to ensure that one does not take priority
Another approach would be to create a Configuration MVC area, and then have a building and group controller in that Area.
You can do that by Attribute Routing in ASP.NET MVC 5. Something like following;
// eg: /reviews
[Route(“reviews”)]
public ActionResult Index() { … }
// eg: /reviews/5
[Route(“reviews/{reviewId}”)]
public ActionResult Show(int reviewId) { … }
// eg: /reviews/5/edit
[Route(“reviews/{reviewId}/edit”)]
public ActionResult Edit(int reviewId) { … }
You can add multiple route for the same controller as well. For details please check here
I code lots of ASP.NET but I'm kind of new with .net MVC, I've a default route registered like this:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
And I want to add another Administrator area on the site and all the URL would be something like "http://localhost/Administrator/controller1", "http://localhost/Administrator/controller2", etc. I've lot of controllers in the Administrator namespace and I'm trying to register those controller with only one MapRoute, I did something like this:
routes.MapRoute("Administrator_default", "Administrator/{controller}/{action}/{id}", new { controller = "Administrator", action = "Index", id = "" });
it works with those controller but one problem is that in some other controller while I try to do a redirect like:
return RedirectToAction("Index", "Forum");
Then I'll always be redirect to http://localhost/Administrator/Forum instead of http://localhost/Forum, it's not a big issue but make the URL looks strange, I tried to restrict to certain namespace but it's not working. It looks just as I'm trying to register two default route and .Net just match the first one, I'm wondering is there a way to make it two default route and map on only specific path only?
This exact issue is why Areas were added to MVC 2. http://www.asp.net/whitepapers/what-is-new-in-aspnet-mvc#_TOC3_2
Agree with Zach's answer.
Not ideal, but you do have the option to have controllers in the controller root folder (e.g. /controllers/HomeController.cs) of your project as well as the controllers in Areas (maybe high level root pages that display menus for areas).
Secondly a quick tip on using the RedirectToAction method. You can specify the area you would like to redirect too using the route parameters e.g:
RedirectToAction("Index","Form", new { area = "MyOtherArea" });
I am looking to produce an MVC site which has complete control of the url structure using routing.
routes.MapRoute(
"BlogView", // Route name
"view/{blogurl}", // URL with parameters
new { controller = "view", action = "view", productLink = ""} // Parameter defaults
);
routes.MapRoute(
"ProductGrid", // Route name
"category/{category}", // URL with parameters
new { controller = "category", action = "Index", category = "" } // Parameter defaults
);
I currently have the follwoing urls;
www.myblog.com/view/first-post
www.myblog.com/view/another-post
www.myblog.com/category/code
www.myblog.com/category/example
The first two urls relate to the detail view, the latter two relating ot a category view.
I have a database with the following structure; I ensure that the url (chrUrl) is a unique key.
url ( idurl (int),
chrURL,
chrAction,
chrController
)
My plan is that it is possible to look up rewrite the route lookup table so that the follwoing urls redirect to the correct view and page in the site;
www.myblog.com/first-post
www.myblog.com/another-post
www.myblog.com/code
www.myblog.com/example
Is this possible? Perofmance aside, is there a problem with this and how shoudl I go about this?
Since you don't have anything to differentiate between view and category items, I'd think about using a default controller which checks if the id is in the categories table and passes control to either the View or the Category controller.
routes.MapRoute(
"Root", // Route name
"/{id}", // URL with parameters
new { controller = "default", action = "redirect"} // Parameter defaults
);
But if you can live with having "/category/" in your category urls, that will be the more elegant solution on the back end.
First up, I would suggest coming up with a URL scheme that you are happy with. (seems you have one already)
Then I would use a ControllerFactory that will be responsible of Instantiating and
running the right action on the right controller. That is independent of any routes that you define in your route table - in fact it wont matter what you have there since you want your URL to be "database driven". You invoke the controller factory from your Global.asax file :
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new Controllers.ControllerFactory());
}
Then in the GetControllerType method in your ControllerFactory, you inspect the URL with
RequestContext.RouteData.Values.ContainsKey("keyname")
to work out the url scheme the user is presenting, and do a database look-up based on that.
If you want to take this one step further, your database can also contain references to the controller to instantiate, but that would be an overkill in your situation. As a quicknote, we use that in a solution where it was important to provide the ability for non-developers to create templates without involving dev - the database held url schemes, controller and views to render on that controller.
While you are at it, if you want to make things more elegant, create a BaseController that your controllers inherit from, and in there set things in your ViewData such as your SEO tags (MetaDescription, Title, etc) - look these up from your database.