Can we make overloaded controller method in ASP .NET MVC - c#

I am developing a ASP .Net MVC project but i can't make overload of Index() even when i have defined other method with different no. of parameters & have given proper routing for it . But it doesnot seems to work. So i just want to ask can we make overloaded methods in controller or not?

Controller actions with the same name are possible on the same controller if they are called with different HTTP verbs. Example:
public ActionResult Index()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(SomeModel model)
{
return View();
}
When you call GET /Controller/Index the first action will be invoked and when you call POST /Controller/Index the second action will be invoked.

To be more specific, you have to vary it by selection criteria (which might be a verbs change as Darin said, but could also be other selector attributes like NonAction or ActionName). For that matter, you could create your own ActionNameSelectorAttribute derivative to create custom logic indicating when a given method should be used over another.
Update: added code per request.
I am actually creating a sample ActionMethodSelectorAttribute, b/c I couldn't think of a good usecase for just testing on the name that's not already covered by the ActionNameAttribute. Principle is the same either way, though.
public class AllParamsRequiredAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
var paramList = methodInfo.GetParameters().Select(p => p.Name);
foreach (var p in paramList)
if (controllerContext.Controller.ValueProvider.GetValue(controllerContext, p) == null) return false;
return true;
}
}
Basically, this one just gets the names of the params on the action method that it flags, and tests to make sure that the controller's ValueProvider has at least an attempted value of the same name as each. This obviously only works for simple types and doesn't test to make sure the attempted value can cast properly or anyting; it's nowhere close to a production attribute. Just wanted to show that it's easy and really any logic you can return a bool from can be used.
This could be applied, then as follows:
[AllParamsRequired]
public ViewResult Index(int count){/*... your logic ... */}
public ViewResult Index() {/*... more of your logic ... */}
in this example,and default routing, the url mydomain.com/?count=5 will match the first one, and the url mydomain.com/ will match the second one.
Paul

Related

NSwag MSBuild "The method 'get' on path '/api/Account' is registered multiple times"

I'm trying to generate a swagger specification with NSwag.MSbuild but whenever I do it throws me this message:
The method 'get' on path '/api/Account' is registered multiple times
Now the problem is that my methods are route-less as shown below with some examples of the controller
[HttpPost]
[HttpGet]
[AllowAnonymous]
public IActionResult ExternalRegister(string provider, string returnUrl = null)
[HttpGet]
public IActionResult AddLogin(string provider, string returnUrl)
[HttpGet]
[AllowAnonymous]
public ActionResult SignUpConfig()
I understand why it does this but what I don't understand is that doing the same thing in NSwag Studio works, the command I use is $(NSwagExe_Core22) webapi2swagger is there an option so that it generates successfully like NSwag Studio?
In a WebAPI if you have more than one HttpGet or HttpPost etc you should add Route Attribute to distinguish them.
Add HttpGet["{name}"]
Turns out you don't have to specify the routes if you don't want to it has something to do with the Default Url Template:
/DefaultUrlTemplate:"{controller}/{action}/{id?}"
adding {action} solved it for me
In my case, I had already added custom [Route("")] attributes to all the paths. The problem was I had two public helper methods in the controller which NSwag identified as GET methods. Simply making them private (which they should have been anyway) made the problem go away...
In my case I had
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(long byId, string color)
{
/* Do things one way */
}
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string byName, string color)
{
/* Do things another way */
}
The problem was that there were two identically named routes that take in different parameters. This is an overload situation that ASP.NET seems to be perfectly fine with but apparently blows NSwag's mind.
Because this was in legacy code, renaming the methods was not an option for me so I created a single method with optional parameters like so:
[HttpGet, ActionName("Stuff")]
public async Task<Stuff> GetStuff(string color, long? byId = null, string byName = null )
{
if (byId != null)
{
/* Do things one way */
}
else
{
/* Do things another way */
}
}
What helped me in this situation was to set the Route Attribute like this:
[Route("SignUpConfig")] ,[Route("AdLogin")]
If your controller is decorated with
[Route("[controller]")]
then you need you specify separate names
HttpGet("get1") and HttpGet("get2")
Else it will pick if decoration contains action name it it like
Route("[controller]/[action]") or from default route {controller}/{action}/{id?}

How to withdraw code from an action method into helper in Asp.Net MVC 3?

I'm writing my app using Asp.Net MVC 3. In my controller I have two action methods with the very same code apart from one line. Here it is:
[HttpPost]
public ActionResult EditPost(Post post)
{
if (ModelState.IsValid)
{
_postsRepository.UpdatePost(post);
return RedirectToAction("NewsFeed");
}
return View("EditPost", post);
}
[HttpPost]
public ActionResult AddPost(Post post)
{
if (ModelState.IsValid)
{
_postsRepository.UpdatePost(post);
return RedirectToAction("NewsFeed");
}
return View("AddPost", post); // the return view is different
}
So, I want to withdraw all this code into helper method.
What I've already tried:
1) I tried to put all the code into helper method and pass as parameters ModelState.IsValid and View name. And then in AddPost and EditPost I call this helper method instead of code listed above. Here is the new code:
[HttpPost] // also tried without this attribute
public ActionResult HelperPost(Post post, string viewName, bool modelState)
{
if (modelState)
{
_postsRepository.UpdatePost(post);
return RedirectToAction("NewsFeed");
}
return View(viewName, post);
}
[HttpPost] // also tried without this attribute
public void AddPost(Post post)
{
HelperPost(post, "AddPost", ModelState.IsValid);
}
The EditPost code is almost the same. The view name is "EditPost".
When I run the app and AddPost method executes the validation works and the new post is created but this line never executes:
return RedirectToAction("NewsFeed");
So I'm redirected to "AddPost" view again and again.
2) Also tried to redirect to HelperPost method instead of calling it withing AddPost and EditPost. The result is still the same: seems like RedirectToAction("NewsFeed") doesn't execute. (Here I neglected the validation just to simplify the example, cause I would have to create new model with properties: Post post, string viewName, bool modelState). The code:
[HttpPost] // tried without attribute
public void AddPost(Post post)
{
return RedirectToAction("HelperPost", post);
}
[HttpPost] // tried without attribute
public RedirectToRouteResult HelperUpdatePost(Post post)
{
_postsRepository.UpdatePost(post);
return RedirectToAction("NewsFeed");
}
So, How could I refactor my code so my action methods (EditPost and AddPost) would not contain the same chunk of code?
p.s. I need different views for AddPost and EditPost methods cause the "back to content" links in them are different. So, I can't just redirect to the EditPost view from AddPost method.
Thanks for help in advance!
Just put your "back to content" link in the model, then use the same view for both, then you can use the same HttpPost method. Saves having to duplicate everything.
I would solve it like this:
I would withdraw the method implementation into separate private
method. This method will be invoked by each of the public action
methods. Since the View name differs for both methods I would pass
the view name as parameter to the private method.
The private method doesn't need the HttpPostAttribute!
Don't forget to declare Add and Edit action methods as returning
ActionResult! As parameter they will expect only Post, the view name has to be hard-coded into the action methodsiteslf ;-)
I hope this helps.

ASP.net c#, Same name method that takes different argument type [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can you overload controller methods in ASP.Net MVC?
I need to 2 methods that takes different type of argument. so I tried this,
[HttpPost]
public ActionResult ItemUpdate(ORDER ln)
{
// do something
}
[HttpPost]
public ActionResult ItemUpdate(List<ORDER> lns)
{
// Do Something
}
but it does not work.
No error while compiling, but when run, it makes an error.
How I write the code to make that works?
Thanks!
[Edit]
[HttpGet]
public ActionResult ItemUpdate(string name)
{
return Content(name);
}
[HttpGet]
public ActionResult ItemUpdate(int num)
{
return Content(num.ToString());
}
and when I call /Test/ItemUpdate/
it make an error,
Server Error in '/' Application.
The current request for action 'ItemUpdate' on controller type 'OrderController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult ItemUpdate(System.String) on type Ecom.WebUI.Controllers.OrderController
System.Web.Mvc.ActionResult ItemUpdate(Int32) on type Ecom.WebUI.Controllers.OrderController
[EDIT]
It does not match with ORDER even single parameter.
if (lns.GetType() == typeof(ORDER))
{
// always false
}else{
// also I can not cast the object.
ORDER ln = (ORDER)lns; //Unable to cast object of type 'System.Object' to type 'ORDER'
}
Overloaded actions are not supported in MVC. The dispatcher can not tell the difference between the two Actions. You can get around this by giving one of your actions the [HttpGet] attribute and the other one the [HttpPost] attribute.
If that isn't an option (or if you have three or more overloads), you can always dispatch the Action yourself, by using an object parameter and using run time type identification to select the correct function to call. E.g.:
[HttpPost]
public ActionResult ItemUpdate(object arg)
{
if (arg.GetType() == typeof(ORDER))
{
return ItemUpdateOrder((Order)arg);
}
else if (arg.GetType() == typeof(List<ORDER>))
{
return ItemUpdateList((List<Order>)arg);
}
}
public ActionResult ItemUpdateOrder(ORDER ln)
{
//...
}
public ActionResult ItemUpdateList(List<ORDER> lns)
{
//...
}
You can't do that in a controller. You will have to change the second method name.
[HttpPost]
public ActionResult ItemUpdates(List<ORDER> lns)
{
// Do Something
}
public ActionResult ItemUpdates(object myInputValue)
{
if (myInputValue.GetType() == typeof(string)
// Do something
else if (myInputValue.GetType() == typeof(List<ORDER>))
// Do something else
}
You can then cast the object to your type of choice and manipulate normally.
In ASP.NET it's not possible to have overloaded methods without an ActionFilter attribute to distinguish these actions. The reason for this is that the ActionInvoker (a class used inside of the Controller base class to invoke actiosn) cannot determine which method to call since it would need to "ask" a ModelBinder (which are responsible to construct action argument objects) for every overload if the ModelBinder could construct that argument object from the HTTP request passed. For simple scenarios this would work but in more complex scenarios this would fail because the ModelBinder would succeed in binding arguments of multiple overloads. Not to allow overloads in ASP.NET MVC is quite clever design decision.
To solve your problems you can fix ItemUpdate HTTP GET action by just leaving the second action away and having just one action, because a ModelBinder does not mind if a value that is passed as URL parameter for example is a string or an int.
[HttpGet]
public ActionResult ItemUpdate(string name)
{
return Content(name);
}
For the ItemUpdate HTTP POST version I'd recommend to rename one of these actions or to have only one action, the list version because updating a single ORDER is only a specific case of updating multiple ORDER objects.

Avoid hard-coding controller and action names

ASP.NET MVC seems to be encouraging me to use hard-coded strings to refer to controllers and actions.
For example, in a controller:
return RedirectToAction("Index", "Home");
or, in a view:
Html.RenderPartial("Index", "Home");
I don't want hard-coded strings all over my code. What can I do to avoid this?
It sounds to me like you want to use strongly typed redirects. I made a static helper class called RedirectionHelper that has the following method:
public static string GetUrl<T>(Expression<Action<T>> action, RequestContext requestContext, RouteValueDictionary values = null) where T : Controller
{
UrlHelper urlHelper = new UrlHelper(requestContext);
RouteValueDictionary routeValues = ExpressionHelper.GetRouteValuesFromExpression(action);
if (values != null)
foreach (var value in values)
routeValues.Add(value.Key, value.Value);
return urlHelper.RouteUrl(routeValues);
}
The only caveat is that you will have to use the Microsoft.Web.Mvc futures library available out on Nuget.
Now, for your controller, create a base controller that all controllers inherit from that has this method:
protected RedirectResult RedirectToAction<T>(Expression<Action<T>> action, RouteValueDictionary values = null) where T : Controller
{
return new RedirectResult(RedirectionHelper.GetUrl(action, Request.RequestContext, values));
}
Now, in your action, all you have to do is say:
return RedirectToAction<Controller>(x => x.Index());
Likewise, you can write a html extension method that takes in the same parameters and builds your anchor tag.
Like you said above that you wanted, when you change Controller or Action names, your project will break at compile time and show you where the breaks occur. However, this will only occur in the controllers, seeing as how the views don't compile.
Hope this helps!
Look at T4MVC this generates classes so you can have strongly typed actions and controller names. As it's still just a mapping to a string, refactoring won't cause the names in your views to update if you change a controller name for example.
After regenerating you will get compilation errors due to the names disappearing from your generated classes though so it's still useful in refactoring and catches problems that you can miss using hard-coded strings.
try T4MVC: http://mvccontrib.codeplex.com/wikipage?title=T4MVC
Not sure if somebody already added an extension method to one of the ASP.NET MVC related projects but here's a piece of code that you can use to create your own extension method:
public RedirectToRouteResult RedirectToAction<TController>(Expression<Action<TController>> action, RouteValueDictionary routeValues) where TController : Controller
{
RouteValueDictionary rv = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);
return RedirectToAction((string)rv["Action"], (string)rv["Controller"], routeValues ?? new RouteValueDictionary());
}
public ActionResult Index()
{
return RedirectToAction<DashboardController>(x => x.Index(), null);
}
There's no parameters merging logic, so you'll have to add it by your own.
UPDATE: #mccow002 added a similar solution a few seconds before me, so I think his solution should be accepted.
I know this is an old topic, but when I was looking for an answer for ASP.NET 5 this theme first appeared.
There is no need to hardcode anymore, just use nameof
[HttpGet]
public IActionResult List()
{
...
return View();
}
[HttpPost]
public IActionResult Add()
{
...
return RedirectToAction(nameof(List));
}

Only allow access to action if redirected from specific action

Is there a good way to restrict the access to an action, so you can only access it, if you were redirected from another action. For example:
[HttpPost]
public virtual ActionResult Create(MyViewModel vm)
{
if (ModelState.IsValid)
{
// do some work
return RedirectToAction("CreateSuccess");
}
else
{
return View(vm);
}
}
public virtual ActionResult CreateSuccess()
{
// only allow execution if you were redirected from Action "Create"
}
An easy way would be to store a flag in TempData in the first method and check that the flag exists in the method that is redirected to.
TempData is there to pass state information between action requests and will only last the duration of the request so you will not need to worry about clearing it down.
There is no way to know the "from" action unless you include parameters indicating such. The easiest way is to append a "SourceAction" or "FromAction" parameter and check it in the "destination" action.
The question is, why do you want to do that? Maybe there is a better solution for your primary problem.
Anyway, you can use the HttpContext.Current.Request.UrlReferrer Property to check the previous page Url.
First solution
You could just do this:
[HttpPost]
public virtual ActionResult Create(MyViewModel vm)
{
if (ModelState.IsValid)
{
// do some work
return this.CreateSuccess();
}
else
{
return View(vm);
}
}
[NonAction]
public virtual ActionResult CreateSuccess()
{
// do what's needed
}
This last method will only be executed from other action methods. But it can't be executed per se.
Second solution
You could solve this by creating a custom action method selector attribute as well if you know you can reuse this. You could write a custom action method selector attribute that checks request referrer and uses appropriate method.
Read about custom action selector attributes.

Categories