MVC 4 - routes turn into a query string - asp.net-mvc

I downloaded the Chinook sample database and I'm trying to build a simple application to teach myself MVC 4 and EF. Chinook is basically a bunch of artists, their albums and tracks in each album. I wanted to create a REST-like URL structure for displaying each artist, their albums and tracks. This is what I was envisioning:
Chinook/Artist <- List of artists
Chinook/Artist/{id} <- Artist details
Chinook/Artist/{id}/Album <- List of albums for artist
Chinook/Artist/{id}/Album/{id} <- Details for an album
Chinook/Artist/{id}/Album/{id}/Track <- Track listing for album
Chinook/Artist/{id}/Album/{id}/Track/{id} <- Track details
This is what my Route.config looks like:
routes.MapRoute(
name: "chinook_artist",
url: "Chinook/Artist/{id}/{action}",
defaults: new { controller = "Artist", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "chinook_album",
url: "Chinook/Artist/{ArtistId}/Album/{id}/{action}",
defaults: new { controller = "Album", action = "Index", id = UrlParameter.Optional, ArtistId = UrlParameter.Optional }
);
routes.MapRoute(
name: "chinook_track",
url: "Chinook/Artist/{ArtistId}/Album/{AlbumId}/Track/{id}/{action}",
defaults: new { controller = "Track", action = "Index", id = UrlParameter.Optional, ArtistId = UrlParameter.Optional, AlbumId = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
My idea was to have Edit, Delete and Add actions as well for all three types of objects, and Index would serve as listing.
This works well enough, however this is what my URLs look like:
http://localhost:57264/Album/?ArtistId=8&AlbumId=10&TrackId=70
In all cases I'm using Html.ActionLink with controller, action, etc. To generate links in views. But I can't get my URLs to start with "Chinook" and be "clean" rather than use a query string, like this:
http://localhost:57264/Chinook/Artist/8/Album/10/Track/70
So I'm not sure what I'm doing wrong here. Am I going against the grain of how MVC is supposed to work? Are my routes defined incorrectly?

Looking at the last 2 routes
Chinook/Artist/{id}/Album/{id}/Track
Chinook/Artist/{id}/Album/{id}/Track/{id}
Your route definitions would need to be
//Chinook/Artist/{artistid}/Album/{albumid}/Track
routes.MapRoute(
name: "chinook_track",
url: "Chinook/Artist/{ArtistId}/Album/{AlbumId}/Track",
defaults: new { controller = "Track", action = "Index" }
);
//Chinook/Artist/{artistid}/Album/{albumid}/Track/{id}
routes.MapRoute(
name: "chinook_trackdetails",
url: "Chinook/Artist/{ArtistId}/Album/{AlbumId}/Track/{id}",
defaults: new { controller = "Track", action = "Details", id = UrlParameter.Optional }
);
and your controller
public class TrackController : Controller
{
public ActionResult Index(int ArtistId, int AlbumId)
{
....
}
public ActionResult Details(int ArtistId, int AlbumId, int id)
{
....
}
}
and in the view, generate the links as
#Html.ActionLink("Index", "Index", "Track", new { ArtistId = 5, AlbumId = 3 }, null)
#Html.ActionLink("Details", "Details", "Track", new { ArtistId = 5, AlbumId = 3, id = 7 }, null)
Then if you wanted a route to edit a track in a similar format
//Chinook/Artist/{artistid}/Album/{albumid}/Track/{id}/Edit
routes.MapRoute(
name: "chinook_trackedit",
url: "Chinook/Artist/{ArtistId}/Album/{AlbumId}/Track/{id}/Edit",
defaults: new { controller = "Track", action = "Edit"}
);
public ActionResult Edit(int ArtistId, int AlbumId, int id)
{
....
}
#Html.ActionLink("Edit", "Edit", "Track", new { ArtistId = 5, AlbumId = 3, id = 7 }, null)

Related

Change url asp.net mvc 5

routes.MapRoute(
name: "MyRoute",
url: "{Product}/{name}-{id}",
defaults: new { controller = "Home", action = "Product", name = UrlParameter.Optional , id = UrlParameter.Optional }
);
my routemap and i want my url in product action be like = http://localhost:13804/Wares/Product/name-id
but now is like =
http://localhost:13804/Wares/Product/4?name=name
When defining a route pattern the token { and } are used to indicate a parameter of the action method. Since you do not have a parameter called Product in your action method, there is no point in having {Product} in the route template.
Since your want url like yourSiteName/Ware/Product/name-id where name and id are dynamic parameter values, you should add the static part (/Ware/Product/) to the route template.
This should work.
routes.MapRoute(
name: "MyRoute",
url: "Ware/Product/{name}-{id}",
defaults: new { controller = "Ware", action = "Product",
name = UrlParameter.Optional, id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Assuming your Product action method accepts these two params
public class WareController : Controller
{
public ActionResult Product(string name, int id)
{
return Content("received name : " + name +",id:"+ id);
}
}
You can generate the urls with the above pattern using the Html.ActionLink helper now
#Html.ActionLink("test", "Product", "Ware", new { id = 55, name = "some" }, null)
I know its late but you can use built-in Attribute Routing in MVC5. Hope it helps someone else. You don't need to use
routes.MapRoute(
name: "MyRoute",
url: "{Product}/{name}-{id}",
defaults: new { controller = "Home", action = "Product", name = UrlParameter.Optional , id = UrlParameter.Optional }
);
Instead you can use the method below.
First enable attribute routing in RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
Then in WaresController
[Route("Wares/Product/{name}/{id}")]
public ActionResult Product(string name,int id)
{
return View();
}
Then to navigate write code like this in View.cshtml file
Navigate
After following above steps your URL will look like
http://localhost:13804/Wares/Product/productname/5

ActionLink/RouteLink not using specified actionName

On the Index.cshtml view of EuroController i have an ActionLink that i want to use the "IndexByYear" action of Euro controller:
#Html.ActionLink("Year 2006", "IndexByYear","Euro", new { id = "", year = 2006 }, null)
But the problem is that it goes to the Index() method, even though it's everything set on RouteConfig:
routes.MapRoute(
name: "Default",
url: "{controller}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Euro",
url: "{controller}/{year}/{id}",
defaults: new { controller = "Euro", action = "IndexByYear", year = DateTime.Now.Year, id = UrlParameter.Optional }
);
Here's the EuroController:
public ActionResult Index(int? id)
{
...
}
public ActionResult IndexByYear(int? id, int year)
{
...
}
Also this don't work, as it also goes to Index() method:
#Html.RouteLink("Ano 2006","Euro",new { id = "", year = 2006 },null)
If i manually navigate to domain/Euro/2016/1, then it uses the correct route. Seems like with no parameters it goes through the Default route.
My question is, why does the ActionLink don't use the IndexByYear as specified, or the RouteLink use the specified default (Euro) route?
If you want just the EuroController to have a "special" routing, you would want to do something like:
routes.MapRoute(
name: "Euro",
url: "euro/{year}/{id}",
defaults: new { controller = "Euro", action = "IndexByYear", year = DateTime.Now.Year, id = UrlParameter.Optional }
);
routes.MapRoute(
name: "DefaultNoController",
url: "{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
constraints: new { isMethodInHomeController = new RootRouteConstraint<Controllers.HomeController>() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Where the constraint is defined like so:
public class RootRouteConstraint<T> : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var rootMethodNames = typeof(T).GetMethods().Select(x => x.Name.ToLower());
return rootMethodNames.Contains(values["action"].ToString().ToLower());
}
}
This will match
domain/euro/1 to the Index action on EuroController
domain/euro/2008/1 to the IndexByYear action on EuroController
domain/ to the Index action on HomeController
domain/about to the About action on HomeController
domain/someothercontroller to the Index action on whatever other controllers you define
You may want to read https://msdn.microsoft.com/en-us/library/cc668201.aspx and https://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/creating-a-custom-route-constraint-cs

ActionLink not working as expected in MVC 4

My html code is as follows
<ul>
#foreach (Department department in #Model)
{
<li>
#Html.ActionLink(department.Name, "Index", "Employee", new { id = department.DeptId }, null)
</li>
}
</ul>
After this when i hover on the link rendered on browser it shows http://localhost/demo/department/index
but when i change the index to Details in the actionLink parameter , then when i hover the link it shows http://localhost/demo/Employee/Details?id=2
Why in the first case instead of this http://localhost/demo/Employee/Index?id=2 , http://localhost/demo/department/index is coming.
I am very new to mvc. Please bear if this question is silly.
Please help me.
UPDATE
My route file is
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{name}/{id}",
defaults: new { controller = "Home", action = "Index", name = UrlParameter.Optional, id = UrlParameter.Optional }
);
routes.MapRoute(
name: "GetCountries",
url: "{controller}/GetCountries",
defaults: new { controller = "Home", action = "GetCountries" }
);
routes.MapRoute(
name: "GetEmployeeDetailsOnId",
url: "Employee/Details",
defaults: new { controller = "Employee", action = "Details" }
);
}
Solution
routes.MapRoute(
name: "GetEmployeeDetails",
url: "Employee/Index/{deptId}",
defaults: new { controller = "Employee", action = "Index", deptId = UrlParameter.Optional }
);
Added this in route and its working.
In the first case your parameters to ActionLink are
ActionName = "Details"
ControllerName = "Employee"
In the second case your parameters to ActionLink are
ActionName = "Index"
ControllerName = "Employee"
These parameters are then matched against your routes one by one.
In the first case there is a match against your third route (url: "Employee/Details")
In the second case there is a match against your first route (url: "{controller}/{action}/{name}/{id}")
For more information about how parameters are matched against routes, please see the link provided by #renjith in the comments: HTML.ActionLink method

MVC Action has the same name, but diffrent controller

Routemap structure:
routes.MapRoute(
name: "NaturalStonesDetails",
url: "{lang}/natural-stones/{title}-{id}",
defaults: new { lang = "en", controller = "NaturalStones", action = "Details" }
);
routes.MapRoute(
name: "ProductCategorieList",
url: "{lang}/products/{title}-{id}",
defaults: new { lang = "en", controller = "Product", action = "Index" }
);
Link structure:
<a href="#Url.Action("Index", "Product", new { title = stoneguide.com.Models.DealerProduct.GetTitleUrlFormat(items.CategoryName), id = Convert.ToInt32(items.ID) })" style="padding:2px;">
Problem:
When I click on the link, go to the product page, which should go to NaturalStones page. I can not solve this problem, a kind.
Please help!
Your routing is quite neat and should work just fine with the code provided. I think you just got confused by which controller to use. So
#Url.Action("Index", "Product", new { title = "mytitle", id = "myid" })
returns /en/products/mytitle-myid which the routing correctly recognises as the request to Product controller, Index action with two parameters.
On the other hand
#Url.Action("Details", "NaturalStones", new { title = "mytitle", id = "myid" });
generates /en/natural-stones/mytitle-myid which is interpreted as request to NaturalStones, Details action with two parameters, and that's probably the one you want to use.
On the side note, providing title and id for Product, Index action is a bit awkward. By convention Index action usually returns a list of items, hence a reference to a specific id seems to be out of place. You might consider changing your routing to:
routes.MapRoute(
name: "NaturalStonesDetails",
url: "{lang}/natural-stones/{title}-{id}",
defaults: new { lang = "en", controller = "NaturalStones", action = "Details" }
);
routes.MapRoute(
name: "ProductCategorieList",
url: "{lang}/products",
defaults: new { lang = "en", controller = "Product", action = "Index" }
);
and then have controllers as follow:
public class ProductController : Controller
{
public ActionResult Index()
{
return View();
}
}
public class NaturalStonesController : Controller
{
public ActionResult Details(string title, string id)
{
return View();
}
}

MVC 3 Route problem

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

Resources