MVC Attribute Routing - Default Controller Index with GET and POST - c#

We have an MVC 5.1 project and are using attribute routing. Everything is working fine except the default page which has a login form on it.
[RoutePrefix("Home")]
public class HomeController : BaseController
{
[Route("~/")]
[Route]
[Route("Index")]
[HttpGet]
public ActionResult Index()
{
var model = new LoginViewModel();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(String Username, String Password)
The form is displayed via the GET fine but upon the POST we get...
HTTP Error 405.0 - Method Not Allowed
The page you are looking for cannot be displayed because an invalid method (HTTP verb) is being used.
Normally the default route would handle both the POST and GET fine.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}/{dealerId}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Obviously I am missing something here on the routing for the post on the default route as subsequent posts on other pages work fine.
Has anyone done this?
Thanks,

Ok seems all I have to do is add
[Route("~/")]
[Route]
[Route("Index")]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(String Username, String Password)
Obvious really! Long Day!

Related

Routing for Particular Action Adds 'Home' In Front of Action

I'm having an odd issue with routing in a basic ASP/MVC project.
I have a bunch of nav items setup:
<li>Home</li>
<li>Fire</li>
<li>Law Enforcement</li>
<li>Forensics</li>
<li>Reconstruction</li>
They all work fine, except for the third one, labeled law-enforcement.
When I mouse over this item, the URL is: http://localhost:54003/home/law-enforcement
When I mouse over any other item, the URl is: http://localhost:54003/fire
My Controller setup is:
public ActionResult Index()
{
return View();
}
[Route("~/fire")]
public ActionResult Fire()
{
return View();
}
[Route("~/law-enforcement")]
public ActionResult Law()
{
return View();
}
[Route("~/forensics")]
public ActionResult Forensics()
{
return View();
}
[Route("~/reconstruction")]
public ActionResult Reconstruction()
{
return View();
}
And my route config is:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.LowercaseUrls = true;
routes.MapMvcAttributeRoutes();
routes.MapRoute("Default", "{controller}/{action}/{id}",
new {controller = "Home", action = "Index", id = UrlParameter.Optional}
);
}
When I go to the route the URL specifies, ASP responds with a 404 page not found (as it should). If I go to the route that I know it should be, such as localhost/law-enforcement then the correct View is rendered.
Any ideas why ASP is routing this one particular action incorrectly?
The Razor Url.Action(...) cannot refer to the route defined by the [RouteAttribute] on the controller action; instead it needs to refer to the action's name. So changing my Razor syntax to refer to #Url.Action("law", "home") instead of #Url.Action("law-enforcement", "home") solved the issue.
You can keep your url mapping in either of 2 ways as below:
One way is to decorate your actionresult with attribute as below:
// eg: /home/show-options
[Route("law-enforcement")] //Remove ~
public ActionResult Law()
{
return View();
}
As per docs
otherwise
Just add one more configuration in Route.config file
routes.MapRoute("SpecialRoute", "{controller}/{action}-{name}/{id}",
new {controller = "Home", action = "law-enforcement", id = UrlParameter.Optional}
);
Source

ASP MCV: Index action with optional string parameter

I would like to have an Index action with an optional string parameter. I'm unable to make it work.
I need the following routes:
http://mysite/download
http://mysite/download/4.1.54.28
The first route will send a null model to the Index view, and the second one will send an string with the version number.
How can I define the route and the controller?
This is my route definition:
routes.MapRoute(
name: "Download",
url: "Download/{version}",
defaults: new { controller = "Download", action = "Index", version = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And this is the controller:
public ActionResult Index(string version)
{
return View(version);
}
Why does this not work? I'm not an expert in ASP MVC but this seems to be a very simple problem.
The error
When I go to http://mysite/downloads it works fine
When I go to http://mysite/downloads/4.5.6, the controller is correctly called, and the parameter is correctly passed. But then seems that the view is not found. This is the error I found:
string? will not work because string is not a value type.
You can set a default value to your parameter:
public ActionResult Index(string version="")
{
return View(version);
}
The issue is fixed passing the parameter to the view in the following way:
public ActionResult Index(string version)
{
return View((object)version);
}
Or
public ActionResult Index(string version)
{
return View("Index", version);
}
When you pass a string model to the view, if the model is a string parameter, it is interpreted as the view name due to the following overload method
View(String viewName)
Your Download route is conflicting with your Default route. Comment out the Download route and it will probably work.
BTW you can install RouteDebugger to figure out these kind of problems for yourself.
Your controller is looking for a view with the same name as the version attribute entered in the url (e.g. 4.1.54.28). Are you intentionally looking for a view with that name, in which case it should be in the Views/Download folder or your project. If however you simply want to pass it to the default view as a variable to be used on the page your best off sticking it in a model or you can just stick it in ViewBag if it's a one off.
Also you don't need to use:
Public ActionResult Index(string version)
You can use routedata instead e.g.
Public ActionResult Index()
{
string version = RouteData.Values["Version"].ToString();
ViewBag.version = version;
return View();
}
Hope this of some help
You are not set action name in url like {action}
you can try:
routes.MapRoute(
name: "Download",
url: "Download/{action}/{version}",
defaults: new { controller = "Download", action = "Index", version = UrlParameter.Optional }
);
I'm pretty sure it's because in the View you state it is an optional parameter, but your controller says that it is mandatory. Change the signature of your index method to expect a nullable param
public ActionResult Index(string? version)
{
return View(version);
}
Why not have two methods in your download controller:
public ActionResult Index()
{
return View();
}
[HttpGet, ActionName("Index")]
public ActionResult IndexWithVersion(string version)
{
return View(version);
}

ASP.NET MVC 5 Attribute Routing Failing To Resolve Route Correctly

I have the following routes in my controller. One Get and two post actions. All have the same action name. The two post actions are differentiated using the MultipleButton attribute as explained here:
[RoutePrefix("incidents")]
public sealed class IncidentsController : Controller
{
[HttpGet, Route("action/{id:int?}/{error?}")]
public async Task<ActionResult> Action(int? id, string error = null)
[HttpPost, Route("action"), ActionName("Action"), ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "Accept")]
public async Task<ActionResult> ActionAccept(IncidentActionViewModel incident)
[HttpPost, Route("action"), ActionName("Action"), ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "Reject")]
public async Task<ActionResult> ActionReject(IncidentActionViewModel incident)
}
#Url.Action("Action", "Incidents", new { id = 10 })
The above route gets rendered as shown below. Navigating to this URL works but if I change the 'id' parameter from Nullable to int I start to get errors.
/incidents/action?id=10
It should be rendering as shown below and I should not get errors if I change the 'id' parameter to type int:
/incidents/action/10
What am I doing wrong?
UPDATE
Here are my route registration details:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
As a work around, I found that I could use named routes instead of relying on ASP.NET MVC to resolve the routes using the controller and action names. So I ended up with the following code:
[RoutePrefix("incidents")]
public sealed class IncidentsController : Controller
{
[HttpGet, Route("action/{id:int?}/{error?}", Name = "IncidentsGetAction")]
public async Task<ActionResult> Action(int? id, string error = null)
[HttpPost, Route("action", Name = "IncidentsPostActionAccept"), ActionName("Action"), ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "Accept")]
public async Task<ActionResult> ActionAccept(IncidentActionViewModel incident)
[HttpPost, Route("action", Name = "IncidentsPostActionReject"), ActionName("Action"), ValidateAntiForgeryToken]
[MultipleButton(Name = "action", Argument = "Reject")]
public async Task<ActionResult> ActionReject(IncidentActionViewModel incident)
}
// Generate a URL
#Url.RouteUrl("IncidentsGetAction", new { id = 10 })
// Generate a link to a URL
#Html.RouteLink("Link Text", "IncidentsGetAction", new { id = 10 })
This approach has better performance as, MVC does not have to resolve the URL, it can go to it directly. It is also easier to understand and reduces the chances of bugs arising as you are being explicit about the route you want to use.
I personally have gone as far as converting all my routes to named routes and have abandoned using controller and action resolved routing. It would still be interesting to see why the above problem occurs.

moving from CodeIgniter to ASP.NET - don't understand default controller actions

I am familiar with CodeIgniter so I'm not a newbie, but I sure feel like one as I move to MVC using C#/.NET. I would expect calling the below controller would properly bind the slash-delimited arguments of URL to the controller input params. Eg: I would expect http://localhost/Download/51 to give fileID=51. However, when I run this, fileID is null when controller is called. Either HttpGet needs querystring with ? and & or need to modify routes somehow to bind properly. Help, please?
[HttpGet]
public ActionResult Download(String fileID)
{
....// http://localhost/Download/51
}
Either use id which is the default route parameter name configured in your Global.asax:
[HttpGet]
public ActionResult Download(string id)
{
....// http://localhost/Download/51
}
or change your routing configuration so that fileID is used:
routes.MapRoute(
"Default",
"{controller}/{action}/{fileid}",
new { controller = "Home", action = "Index", fileid = UrlParameter.Optional }
);
Also checkout the following for more details on routing.

Using the ASP.NET MVC Attribute Based Route Mapper

In my ASP.NET MVC application, I want to use this ASP.NET MVC Attribute Based Route Mapper, first announced here.
So far, I understand how to implement code that uses it, but I've run into a few questions that I think those who have used this attribute-based route mapper in the past will be able to answer.
How do I use it with ActionResults that are for HTTP POSTs? In other words, how does it work with form submissions and the like? Should I just put the URL of the GET method in, or should I use the GET method URL without any parameters (as in HTTP POST they aren't passed in through the URL)?
How do I use it with "URL querystring parameters"? Can the attribute be configured to map to a route such as /controller/action?id=value rather than /controller/action/{id}?
Thanks in advance.
How do I use it with ActionResults
that are for HTTP POSTs?
You decorate the action that you are posting to with the [HttpPost] attribute:
[Url("")]
public ActionResult Index() { return View(); }
[Url("")]
[HttpPost]
public ActionResult Index(string id) { return View(); }
If you decide to give the POST action a different name:
[Url("")]
public ActionResult Index() { return View(); }
[Url("foo")]
[HttpPost]
public ActionResult Index(string id) { return View(); }
You need to supply this name in your helper methods:
<% using (Html.BeginForm("foo", "home", new { id = "123" })) { %>
How do I use it with "URL querystring
parameters"?
Query string parameters are not part of the route definition. You can always obtain them in a controller action either as action parameter or from Request.Params.
As far as the id parameter is concerned it is configured in Application_Start, so if you want it to appear in the query string instead of being part of the route simply remove it from this route definition:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoutes();
routes.MapRoute(
"Default",
"{controller}/{action}",
new { controller = "Home", action = "Index" }
);
}

Categories

Resources