we have decided to use subfolders in our Controllers folder. All our controllers are working like they should, but for one reason or another it doesn't get in the Controller method when we add an {id}
Route:
routes.MapRoute(
name: "Maintenance",
url: "Maintenance/{controller}/{action}/{id}",
defaults: new { controller = "TargetAudience", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "IstPhenix.Controllers.Maintenance" }
);
This matches URLs like
/Maintenance/Category/Index
but whenever we add the {id} like /Maintenance/Category/Edit/123456789 the Controller method cannot be found
CategoryController.cs:
[ClaimsAuthorize(PhenixPermissions = "ConsultCategory")]
public ActionResult Edit(int id)
{
ViewBag.CategoryId = id;
return View("~/Views/Maintenance/Category/Edit.cshtml", id);
}
[ClaimsAuthorize(PhenixPermissions = "ConsultCategory")]
public ActionResult Index()
{
return View("~/Views/Maintenance/Category/Index.cshtml");
}
Usefull to mention is that we also have a default route at the end of our RouteConfig:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new string[] { "IstPhenix.Controllers.Enduser", "IstPhenix.Controllers.Maintenance", "IstPhenix.Controllers.Security" }
);
And then following URLs work when we omit the subfolder:
/Category/Index
/Category/Edit/123456789
Any idea why the URL does not match the route when i have a subfolder and an id?
What am i missing?
tnx!
What if you added a Route attribute:
[Route("Maintenance/Category/Edit/{id}")]
[ClaimsAuthorize(PhenixPermissions = "ConsultCategory")]
public ActionResult Edit(int id)
{
ViewBag.CategoryId = id;
return View("~/Views/Maintenance/Category/Edit.cshtml", id);
}
That would explicitly give the route to the action.
Related
I am invoking the url "localhost/Student/1" to return the student with id=1. My route config:
routes.MapRoute(
name: "StudentID",
url: "Student/{id}",
defaults: new { controller = "Student", action = "Get}
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Student", action = "Index", id = UrlParameter.Optional }
);
public class StudentController : Controller
{
// GET: Student
[Authorize]
public ActionResult Index()
{
SchoolDbEntities db = new SchoolDbEntities();
return View(db.Students);
}
[Route("Student/{id}")]
[ActionName("Get")]
public ActionResult Get(int id)
{
SchoolDbEntities db = new SchoolDbEntities();
var student = db.Students.Where(s => s.Id == id);
return View(student);
}
}
I configured both route table and use route attributes.
However when i run the application it throws an error:
The resource cannot be found.
how do i implement the same as using webapi.
If you want to use attribute routes in MVC you must register them by calling routes.MapMvcAttributeRoutes() in your RouteConfig class.
Note that the registration order matters. By putting the call to route.MapMvcAttributeRoutes() before your routes.MapRoute() call MVC will look at the attribute routes before the traditional routes. The first matching route wins.
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Student", action = "Index", id = UrlParameter.Optional }
);
In your controller setup your attribute routing like this:
[RoutePrefix("Student")]
public class StudentController : Controller
{
// GET: Student
[Authorize]
public ActionResult Index()
{
SchoolDbEntities db = new SchoolDbEntities();
return View(db.Students);
}
[Route("{id}")]
public ActionResult Get(int id)
{
SchoolDbEntities db = new SchoolDbEntities();
var student = db.Students.Where(s => s.Id == id);
return View(student);
}
}
This will match the URL "Student/1".
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
I am getting url like http://localhost:49671/TestRoutes/Display?f=hi&i=2
I want it like http://localhost:49671/TestRoutes/Display/hi
I call it from Index method.
[HttpPost]
public ActionResult Index(int? e )
{
// return View("Display", new { f = "hi", i = 2 });
return RedirectToAction("Display", new { f = "hi", i = 2 });
}
Index view
#model Try.Models.TestRoutes
#using (Html.BeginForm())
{
Model.e = 5 ;
<input type="submit" value="Create" class="btn btn-default" />
}
Display Action method
// [Route("TestRoutes/{s}")]
public ActionResult Display(string s, int i)
{
return View();
}
Route config file
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Professional", // Route name
"{controller}/{action}/{id}/{name}", // URL with parameters
new { controller = "TestRoutes", action = "Display", s = UrlParameter.Optional, i = UrlParameter.Optional
});
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional
});
You need to change your route definition to
routes.MapRoute(
name: "Professional",
url: "TestRoutes/Display/{s}/{i}",
default: new { controller = "TestRoutes", action = "Display", i = UrlParameter.Optional }
);
so that the names of the placeholders match the names of the parameters in your method. Note also that only the last parameter can be marked as UrlParameter.Optional (otherwise the RoutingEngine cannot match up the segments and the values will be added as query string parameters, not route values)
Then you need to change the controller method to match the route/method parameters
[HttpPost]
public ActionResult Index(int? e )
{
return RedirectToAction("Display", new { s = "hi", i = 2 }); // s not f
}
change your route as
routes.MapRoute(
"Professional", // Route name
"{controller}/{action}/{name}", // URL with parameters
new
{
controller = "TestRoutes",
action = "Display"
} // Parameter defaults
);
and your action as
public ActionResult Display(string name)
{
//action goes here
}
Remove the maproute code:
routes.MapRoute(
"Professional", // Route name
"{controller}/{action}/{id}/{name}", // URL with parameters
new { controller = "TestRoutes", action = "Display", s = UrlParameter.Optional, i = UrlParameter.Optional
});
Use attribute routing code:
[Route("TestRoutes/{s}/{i?}")]
public ActionResult Display(string s, int? i)
{
return View();
}
You can also try using Attribute Routing. You can control your routes easier with attribute routing.
Firstly change your RouteConfig.cs like that:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
//routes.MapRoute(
// name: "Default",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
//);
}
}
After that change your controller files like that:
namespace YourProjectName.Controllers
{
[RoutePrefix("Home")]
[Route("{action}/{id=0}")]
public class HomeController : Controller
{
[Route("Index")]
public ActionResult Index()
{
return View();
}
[Route("ChangeAddress/{addressID}")]
public ActionResult ChangeAddress(int addressID)
{
//your codes to change address
}
}
You can also learn more about Attribute Routing in this post:
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
Another way to solve this problem is to put the proper route before the default route, as follows:
routes.MapRoute(name: "MyRouteName", url: "Id", defaults: new { controller= "Home", action = "Index",id= Id });
Default route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{Id}",
defaults: new { controller = "Home", action = "Index",id= Id }
);
I have the simplest setup:
An empty asp.net MVC application with one controller:
public class HomeController : Controller
{
public ActionResult Edit(int id)
{
return View();
}
public ActionResult Commit(int id)
{
return View();
}
}
My Edit.cshtml has a call to ActionLink() like so:
#Html.ActionLink("Commit Data", "Commit")
If I now access the Edit-Action through "/Home/Edit/2" I would expect that the rendered link directs the user to "/Home/Commit/2".
It does not :( ... The link is created to "Home/Commit", completely disregarding the current RouteData entries.
I am using the default routing configuration (have not added any routes).
One way to fix this would be to add an explicit route for both actions:
routes.MapRoute(
name: null,
url: "Home/Edit/{id}",
defaults: new { controller = "Home", action = "Edit" }
);
routes.MapRoute(
name: null,
url: "Home/Commit/{id}",
defaults: new { controller = "Home", action = "Commit" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This works - but I really dont want to explicitly define every single route in the app - if I am using the "default" pattern...
The second solution would be to just add the routing-values manually like so:
#Html.ActionLink("Commit Data", "Commit", "Home", new {id = Model.Id})
But this also seems not right - ActionLink SHOULD use the current routing information, should it not?
What am I missing?
Ok, in case someone else is wondering the same thing - or just wants to have a solution that works...
I simply created my own #ActionLink() helper method on my custom ViewPage base class:
protected MvcHtmlString ActionLink(string linkText, string actionName)
{
var routeData = ViewContext.RequestContext.RouteData;
var id = routeData.Values["id"];
if (id != null)
return Html.ActionLink(linkText, actionName, new {id = id});
return Html.ActionLink(linkText, actionName);
}
This is exactly what I wanted. Now I can call
#ActionLink("Commit", "Commit")
and when I'm in the context of something with an id, the link will point to the appropriate route for this Id.
I've got a simple MVC4 web site containing an area (called "User") containing a Controller called "HomeController".
On this controller there are two action methods: Index and Details:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Details(int id)
{
return View();
}
}
The "UserAreaRegistration.cs" class is as follows:
public class UserAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "User";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"User_default",
"User/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
context.MapRoute(
"User_default_no_contoller",
"User/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
}
}
There's also a Controller that's not in an area called "HomeController":
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
RouteConfig.cs (for non-area routes) is as follows:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Controllers" }
);
}
}
I am able to access the "homepage" via either
/
/Home
/Home/Index
I'm also able to get to the "Index" action in my "User" area
/User
What I cannot do is work out how to get to the other method on my Controller in the Area called "Details":
/User/Details/5 does not work, it returns a 404
I also notice that trying to access the Index method explicitly is also not routed correctly:
/User/Index does not work, it returns a 404
What am I doing wrong?
Is it possible to get to any action method other than "Index" on a controller that's the "default" controller in an area?
There are too many defaults on your first route, so it's swallowing everything up.
the URI /User/Details/5 matches the User_default route (Controller: "Details" and Action: "5").
the URI /User/Index also matches the first route (Controller: "Index" and the default Action: "Index")
The way to approach routes is
put the most selective route first
and set only the minimum default values necessary
use route constraints to ensure that the MVC router doesn't attempt to match parameters incorrectly (ie, give route values intended for int parameters a numeric constraint)
put the most general route (ie, User_default) last (as a catch-all for URIs that didn't match any more specific route)
I'd try setting up the area routes like this.
context.MapRoute(
"User_default_no_contoller",
"User/{action}/{id}",
defaults: new { controller = "Home", id = UrlParameter.Optional },
constraints: new { id = #"\d+" },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
context.MapRoute(
"User_default",
"User/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new[] { "MvcApplication1.Areas.User.Controllers" }
);
Another suggestion to overcome this "Multi-defaults" problem is to separate using Namespaces.
In your example my suggestion is to have the Class UserAreaRegistration in the namespace "Areas.User" . Subsequently you would have to put your HomeController in the User Area in the Namespace "Areas.User.Controllers"
It works for me and keeps the code clean.
Peter
Thanks for the answer dbaseman. It helped me look at my own routing problem. I am not sure if I was running into the same issue but that is what it seems like.
routes.MapRoute("Tag", "Tag/{tag}", new { controller = "Blog", action = "Tag" });
routes.MapRoute("Category", "Category/{category}", new { controller = "Blog", action = "Category" });
routes.MapRoute("Post", "Archive/{year}/{month}/{day}/{title}", new {controller = "Blog", action = "Post"});
routes.MapRoute("Action", "{action}", new { controller = "Blog", action = "Posts" });
When I had the last route with the "Action" parameters above any of the other routes the ones below "Action" would not be caught. Moving it to the bottom of the RouteConfig allowed all the ones above it to be hit successfully now.