MVC Route conflict seems strange - asp.net-mvc

I am using MVC 5.1 with AutoFac.
I dont understand why the below route from each controller conflict with this URL: https://localhost:44300/Home/login
I thought it would map to the first method. I get this error though:
Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
The request has found the following matching controller types:
AllThings.WebUI.Controllers.AccountController
AllThings.WebUI.Controllers.PostController
public class AccountController : Controller
{
//
// GET: /Account/Login
[Route("~/{site}/Login")]
[Route("~/Account/Login")]
[Route("~/{country:maxlength(2)}/{site}/Login")]
[Route("~/{country:maxlength(2)}/Account/Login")]
[AllowAnonymous]
public ActionResult Login(string returnUrl, string country, string site)
{
return View();
}
}
public class PostController : Controller
{
[Route("~/{site}/{CategoryUrl?}")]
[Route("~/{country:maxlength(2)}/{site}/{CategoryUrl?}", Name = "ResultList")]
[AllowAnonymous]
public ActionResult List(string country, string site, SearchCriteriaViewModel searchCriteriaViewModel)
{
return View("List", searchCriteriaViewModel);
}
}

The main problem is that you have 3 possible routes that can match /Home/Login.
[Route("~/{site}/Login")]
[Route("~/Account/Login")]
[Route("~/{site}/{CategoryUrl?}")]
Liberal use of placeholders, especially that is all you have in a URL template definition is not a good thing. You should use literals in the URL or if you use placeholders, there should be constraints on them so they don't conflict.
Note that the following routs conflict as well:
[Route("~/{country:maxlength(2)}/{site}/Login")]
[Route("~/{country:maxlength(2)}/Account/Login")]
[Route("~/{country:maxlength(2)}/{site}/{CategoryUrl?}", Name = "ResultList")]
Any one of them could match UK/Account/Login.
Additionally, using a tilde (~) is to override a route prefix (see the MSDN documentation). If your controller doesn't define one, you should just start with the first segment or placeholder.

Related

Attribute routing in MVC

Can anyone explain me about attribute routing. I am having a question about it.
When I am using "Only" attribute routing I am getting below error.
When URL is "http://localhost:51254/":
HTTP Error 403.14 - Forbidden The Web server is configured to not list
the contents of this directory.
When URL is "http://localhost:51254/MyHome/HomeAction"
HTTP Error 404.0 - Not Found The resource you are looking for has been
removed, had its name changed, or is temporarily unavailable.
My code in controller:
[RoutePrefix("MyHome/{action}")]
public class IndexController : Controller
{
[Route("HomeAction")]
public ActionResult Index()
{
return View();
}
[Route("CallUs")]
public ViewResult ContactUs()
{
return View();
}
}
And in RouteConfig.cs look like:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
Is there anything wrong with URL?
I tried in different way like:
http://localhost:51254/Index
http://localhost:51254/Index/Index
http://localhost:51254/HomeAction
http://localhost:51254/MyHome
http://localhost:51254/MyHome/HomeAction
So if I refraim question then it will be like:
Is it mandatory to use convention based routing and objRouteCollection.mapRoute method?
because if I add MapRoute method it work quite good.
I searched but couldn't find anything that answer my question. For Example msdn, c-SharpCorner
When an action method is decorated with Route attribute , it can no longer be accessed from convention based routes defined in RouteConfig.cs
MVC expects literal string in RoutePrefix, else it will give runtime error. Actual error : "A direct route for an action method cannot use the parameter 'action'. Specify a literal path in place of this parameter to create a route to the action."
So remove {action} from RoutePrefix.
Now if you access 'http://localhost:xxxxx/MyHome/HomeAction', it should work.

Route attribute hides actions

Why Route attribute hides|applied to other methods?
[HttpGet]
[Route("Issues/ReportIssue")]
public ActionResult ReportIssue()
Action without Route attribute stops working
[HttpPost]
public ActionResult ReportIssue(IssueModel viewModel)
jQuery returns "Not found" error
If rename Post action or add Route attribute, all works. Is there are any priority rules for a Route attribute?
I tried following code sample -
[HttpGet]
[Route("Products/Create")]
public ActionResult Create()
{
ProductCategory category = new ProductCategory();
category.ProductCategoryId = 1;
return View(category);
}
[HttpPost]
public ActionResult Create(ProductCategory category)
{
return View(category);
}
And I could see this working properly without any issues. The controller name in my case is "Product", and the below code works if I specify route attribute as "Products/Create" (different from controller name), or "Product/Create". So not sure, why you would be facing the issue.
The Get and Post actions should work without route attribute as well, as they were working. Attribute routing is something newly added in MVC 5, and is there to give more control over the routes. But still, this is not mandatory to use. The traditional routes should work fine.
Take a look of - http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx article.

How can I overload ASP.NET MVC Actions based on the accepted HTTP verbs?

Wanted to use the same URL for a GET/PUT/DELETE/POST for a REST based API, but when the only thing different about the Actions is which HTTP verbs it accepts, it considers them to be duplicate!
"Type already defines a member called 'Index' with the same parameter types."
To which I said, so what? This one only accepts GET, this one only accepts POST... should be able to be co-exist right?
How?
That's not ASP.NET MVC limitation or whatever. It's .NET and how classes work: no matter how hard you try, you cannot have two methods with the same name on the same class which take the same parameters. You could cheat using the [ActionName] attribute:
[HttpGet]
[ActionName("Foo")]
public ActionResult GetMe()
{
...
}
[HttpPut]
[ActionName("Foo")]
public ActionResult PutMe()
{
...
}
[HttpDelete]
[ActionName("Foo")]
public ActionResult DeleteMe()
{
...
}
[HttpPost]
[ActionName("Foo")]
public ActionResult PostMe()
{
...
}
Of course in a real RESTFul application the different verbs would take different parameters as well, so you will seldom have such situations.
You may take a look at SimplyRestful for some ideas about how your routes could be organized.
While ASP.NET MVC will allow you to have two actions with the same name, .NET won't allow you to have two methods with the same signature - i.e. the same name and parameters.
You will need to name the methods differently use the ActionName attribute to tell ASP.NET MVC that they're actually the same action.
That said, if you're talking about a GET and a POST, this problem will likely go away, as the POST action will take more parameters than the GET and therefore be distinguishable.
So, you need either:
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost, ActionName("ActionName")]
public ActionResult ActionNamePost() {...}
Or:
[HttpGet]
public ActionResult ActionName() {...}
[HttpPost]
public ActionResult ActionName(string aParameter) {...}
Another option is to have a single method that accepts all and distinguishes between HttpMethod and calls the appropriate code from there. E.g.
string httpMethod = Request.HttpMethod.ToUpperInvariant();
switch (httpMethod)
{
case "GET":
return GetResponse();
case "POST":
return PostResponse();
default:
throw new ApplicationException(string.Format("Unsupported HttpMethod {0}.", httpMethod));
}
As a workaround you can add to one of the methods an extra argument with a default value, just to bypass the limitation and be able to build.
Of course take in mind that this is not the most recommended way of doing things, and also you will have to make clear in your code (by the parameter name or via comments) that this is an extra argument just to allow it to build, and of course make sure that you have decorated your attributes correctly.

How to change action parameters and get it to work without changing routing in asp.net mvc?

In my route I have something like this:
controller/action/{id}
To my knowledge this means it will call any action with the parameter id like the following:
public ActionResult Detail(string id)
{
}
What do I have to do to make the following work without registering the particular route in global.asax file:
public ActionResult Detail(string customerId)
{
}
If you really don't want to rename the method parameter, you can use BindAttribute to tell MVC what its logical name should be:
public ActionResult Detail([Bind(Prefix = "id")] string customerId)
You can also pass customerId as query string, which is normally what I do:
<%: Html.ActionLink("Detail", "Detail", new { #customerId = Model.CustomerID}, null)%>
MVC does not enforce the routing, but rather try to resolve routing based on your url AND query string.
have a route like - controller/action/{customerId} or just rename the parameter customerId to id and then use it the particular way you want.

MVC Post with dynamic routing

I've created a routing structure whereas the action part of the URL serves as a dynamic handler for picking a specific user created system name. i.e.
http://mysite.com/Systems/[SystemName]/Configure, where [SystemName] designates the name of the system they would like to configure.
The method that routes the system is the following:
public ActionResult Index(string systemName, string systemAction)
{
ViewData["system"] = _repository.GetSystem(systemName);
if (systemAction != "")
{
return View(systemAction);
}
else
{
// No Id specified. Go to system selection.
return View("System");
}
}
The above method sets the system to configure and routes to a static method where the view is displayed and a form awaits values.
The question I have is that when I create my configuration view, I lose my posted values when the form is submitted because it routes back to the above Index controller. How can I determine if data is being posted when hitting my above Index controller so that I can make a decision?
Thanks!
George
Annotate the controller method that handles the POST like this:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(string systemName, string systemAction)
{
// Handle posted values.
}
You can have a different method in your controller that handles the GETs:
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index(string systemName, string systemAction)
{
// No posted values here.
}
Note that, although I have copied the same method and parameters in each case, the signature for the second method (parameters and types) will have to be different, so that the two methods are not ambiguous.
The NerdDinner tutorial has examples of this.

Resources