Overloading in MVC - asp.net-mvc

I read here that I can't overload actions in MVC because of routing confusion
I tried to overload Index() in HomeController and I got the exception as article said, but I noticed that
microsoft has overloaded the actions in AccountController
public ActionResult Login(string returnUrl){}
public ActionResult Login(LoginModel model, string returnUrl){}
Please need clarification, thanks

Microsoft has overloaded this by setting HttpGet and HttpPost. One for GET request and another for POST request. What about your code?
[HttpGet]
public ActionResult Login(string returnUrl){}
[HttpPost]
public ActionResult Login(LoginModel model, string returnUrl){}

Till today you cannot overload your controller's Action method with same name but different parameters.
The only possibility is to have two overload, and for that you need to set the method's property to HttpGet and HttpPost. For example
[HttpGet]
public ActionResult foo(string myString)
{
return View();
}
[HttpPost]
public ActionResult foo(MyViewModelClass object)
{
return View();
}
And regarding your confusion,
From general convention, first method should be of type Get which gets called when someone sends request to access that page.
Second method is called when user submits a form with his login details.

In AccountController first method works with GET method, second with POST one. It was realized by attribute [HttpGet] and [HttpPost].
Read more about get and post here.

In addition to above answer we can add name attributes along with HTTPGET and HTTPPOST as
[HttpPost]
[ActionName("Edit")]
public ActionResult Edit_Post(some parameters)
{
//code over here
}
After this we can call it as :- /MVC/EmployeeController/Edit/1

Some definitions first:
Overloading is a form of polymorphism, in particular an interface, in the sense of a class publicly visible part, related one.
When we speak about inheritance we mean overriding.
Action is a segment of a URL.
Back to your question...
It is the ControllerActionInvoker which is responsible for finding the method to which an action is mapped. Given GET and POST we see polymorphic methods in a class mapped to the same action, but serving different HTTP methods (any action, that is, URL segment polymorphism here?!). And again yes, we may use ActionNameAttribute (MVC v5) to map an action to a class method, but again, this has nothing to do with any sort of polymorphism. Simply put, all that happens is in the ControllerActionInvoker and has nothing to do with overloading or overriding, and finally with any sort of polymorphism -- it is just mapping.
Conclusion.
A simple question arises:
What in a string (a segment of a URL, 3.) relates to any one of the OOP definitions (1. and 2.) above? Or, in other words, can any of the OOP concepts above be transformed to a (part of a) string?
The fact that a transformation between URL segment, which happen to be called "action", and a class method exits does not imply that all we know about the first mechanically can be applied to the second and vice-verse. The question is misleading and its main intention is to confuse someone, not to test his/her knowledge (I suspect that this is an artificial interview questions, not a real one).

Related

Purpose of public NonAction methods in MVC

i have just started working in MVC and I have one doubt.
Instead of Nonaction method , we can create private method in controller or we can also write method in model and call that from controller.
So , what is the real purpose to use public NonAction method in MVC ?
(I restructured the answer to better address the questions in the comments)
I think, the attribute is here only for better flexibility. As a framework designer, one wants to relax coding constraints off the end user as much as possible. Requirement of not having public non-actions may sound good "in general" but may be too restrictive for some projects. Adding [NonAction] solves their problem (introduced by their bad design though) - and obviously you're not forced to use the attribute, so it's a win-win from a framework designer perspective.
Another reason may be legacy - in the earlier MVC versions only methods marked with [Action] where considered as actions. So when they relaxed the requirement (and all public methods became treated as actions) they kept [NonAction] so that developers won't get too confused.
In general, using NonAction is a bad practice - exactly for the reasons you stated. If something shouldn't be an action, it should not be public in the first place.
Problem with public non-action methods on the controller is that they make people tempted to instantiate your controller and call the method, instead of separating out the common logic:
Compare
public class MyController : IController
{
public ActionResult Foo(long orderId)
{
var order = new OrdersController().GetOrder(orderId); //GetOrder is public
...
}
}
with
public class MyController : IController
{
public ActionResult Foo(long orderId)
{
var order = _orderService.GetOrder(orderId);
...
}
}
The first approach leads to increased coupling between controllers and non-straightforward code in the actions. Code becomes difficult to follow and refactor, and cumbersome to mock/test.
Besides increased coupling, any public non-action method is a security hole - if you forget to mark it with [NonAction] (or, better, change away from public) - because it's treated as normal action and can be invoked externally. I know the original question kinda implies you surely would never forget to attach the attribute if needed, but it's also kinda important to understand what can happen if you would ;) Oh well, and as we're on this, it seems to me that "forgetting the attribute" is more theoretically probable, comparing to "forgetting to make the method private".
Sometimes people say having public non-actions is necessary for unit testing, but again, when something is not an action it most likely can be isolated in a separate class and tested separately. Moreover, even if it's not feasible for whatever reason, marking a method public for testing purposes only is a bad habit - using internal and InternalsVisibleTo is the recommended way.
This kind of situation may be caused by requirements some testing framework such as you need to do unit testing on that method then you to expose it although its a bad design but can't change these had to bear it out.
By default, the MVC framework treats all public methods of a controller class as action methods. If your controller class contains a public method and you do not want it to be an action method, you must mark that method with the NonActionAttributeattribute.
Real purpose to use public NonAction
To restrict access to non-action method to notify MVC framework that given controller method is not action.
When you try to run a method with NonAction attribute over URL you get the error 404 as response to request.
Ref: http://msdn.microsoft.com/en-us/library/dd410269%28v=vs.90%29.aspx
For Detail: http://weblogs.asp.net/gunnarpeipman/archive/2011/04/09/asp-net-mvc-using-nonactionattribute-to-restrict-access-to-public-methods-of-controller.aspx
This is beneficial when the Url are not case sensitive. So that for example if you have the request Home/About this goes to HomeController and About action, as well as hOmE/AbOUT is going to the same controller and same action method.
Like below
public class HomeController:Controller
{
....
public ViewResult About()
{
return View();
}
public ViewResult aBOut()
{
return View();
}
}
The framework can’t determine which about function to call, and throws the exception telling that the call is ambiguous.
Of course one way to fix this problem is to change the action name.
If for some reason you don’t want to change the action name, and one of these function is not an action, then you can decorate this non action method with NonAction attribute. Example:
[NonAction]
public ActionResult aBOut()
{
return View();
}
By default, the MVC framework treats all public methods of a controller class as action methods. If your controller class contains a public method and you do not want it to be an action method, you must mark that method with the NonActionAttribute attribute.
We are using controllers as binding drivers with custom ASP pipeline, each driver is responsible for rendering one section (partial view) of result page. Then we are using public methods like:
[NonAction]
publi int GetOrder()
to resolve sections order on page or other to resolve authorization for current user (e.g. if current section is editable or just read-only).
So you should not restrain yourself to think about Controller as only a way to handle requests but also as a tool to build your custom framework for rendering page. That way we keep our Controllers responsible for exactly one task and we are separating domain concerns.
ASP.NET is highly customizable. Assume you are going to change the default behavior of the framework by overriding the MVC HTTP handler. Maybe you want to customize the logging logic depending on the controller, which is used. Some controllers implement your ILoggingController interface with the method IControllerLogger GetLogger(). For this method you need to write a public non-action method.

Ambiguous action method

For a project I'm currently working on, I currently have 2 separate instances of users (might increase later): CorporateCustomer and PrivateCustomer.
Both inherit from the abstract class Customer.
To display the differences between these customers, currently 2 different views are created, which are rendered by the same Action in the following way:
[HttpGet]
public virtual ActionResult Edit()
{
if(User.IsCorporate)
return View("EditCorporate", new CorporateCustomer());
else
return View("EditPrivate", new PrivateCustomer());
}
[HttpPost]
public virtual ActionResult Edit(CorporateCustomer customer){...}
[HttpPost]
public virtual ActionResult Edit(PrivateCustomer customer){...}
For just displaying information, this works like a charm. The urls are the same for each type, which is what we were aiming for.
However, when doing a post, I can only specify a single type, or risk running into an ambiguous action method (which makes sense, of course).
Now my question is: is there any elegant way to handle these 2 different types, while still retaining a single url? Or am I doomed to make the base class non-abstract and look up the values in the Request.Form collection?
Thanks if anyone can come up with a sollution (or just straight point out that what I'm doing is stupid and cannot be done)
You could have one Action that takes both parameter types.
The model binder should then fill them with whatever data is posted and you can figure out which is right in your Action method.
[HttpPost]
public virtual ActionResult Edit( CorporateCustomer c, PrivateCustomer p )
{
...
}

Is it possible to have duplicate action names and parameter list for post and get?

is it possible to have 2 actions with the same name and parameters but one's a post, the other a get? e.g Delete(id) and [HttpPost]Delete(id)...i get an error saying that this is not allowed...
Yes, it's possible. Just use ActionName attribute on one action:
public ActionResult Delete(int id)
{
//...
return View();
}
[HttpPost]
[ActionName("Delete")]
public ActionResult Delete_Post(int id)
{
//...
return View();
}
The reason you get the error that it is not allowed is because C# itself gets confused. While in MVC you can add attributes to specify whether a function is HttpGet or HttpPost, that doesn't help C# determine the difference between one or the other. In order to have 2 functions with exactly the same name, the parameter list needs to be different.
As frennky pointed out, the ActionName attribute works in MVC because MVC uses aliases as part of the process for determining which action to call (along with attributes, but not parameters).
As a side note, it's probably best not to have a Delete action on a GET request. You don't want a crawler or some other bot accidently hitting the wrong link :P

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.

ASP.NET MVC Preview 5 routing ambiguity

I have a problem with a sample routing with the preview 5 of asp.net mvc.
In the AccountController I have 2 actions:
public ActionResult Delete()
public ActionResult Delete(string username)
While trying to look for Account/Delete or Account/Delete?username=davide the ControllerActionInvoker throws a exception saying that Delete request is ambiguous between my tow actions methods.
The default route in the global.asax hasn't been changed.
Shouldn't the action invoker understand what's the method to call looking in the parameters list?
Using the preview 4 I hadn't these kind of problem performing the same operation.
Any idea?
Solution found!
With the introduction of the ActionNameAttribute, it's now necessary to filter manually which method to call depending on the request. This is done by the ActionSelectionAttribute.
Full explanation here: http://haacked.com/archive/2008/08/29/how-a-method-becomes-an-action.aspx
I can't say for sure why this is happening. But you might want to consider only having the Delete(string username) action and removing the parameter-less overload.
Because string is nullable my understanding is that simply calling Account/Delete will invoke the action with a null username parameter which you can then test for at the beginning of the action method.
What I'd do is ditch the blank Delete(), and only use Delete(string username)
In your url routing you'd have something similar to "/{Controller}/{Action}/{username}/" ?
If you have "/{Controller}/{Action}/{Id}/" you'd be better off doing Delete(string id) and that way just using the url to handle this "/Account/Delete/davide/"
That said use your default route which should be something like the default Id is ""
Then in your Delete(string id) method have:
public ActionResult Delete(string id)
{
if(string.IsNullOrEmpty(id)) return EmptyID();
// Continue normal Delete method
}
public ActionResult EmptyID()
{
// The method you were going to have on a blank delete.
}
That or just wrap it up in the one method on an if {} else {}
Either way I'd just be going with the one method and doing a default on your username/id in your route of an empty string and handle it that way.
If you want to contact me on further follow up to what I mean, or whatever will help, ping me at andrew# my domain on my info page.
Edit: Ah pretty much what Berko said anyway, I'm not sure how Named Attributes would help - so please post a comment here detailing it for other guys who find the same issues! :)
Its ambiguous because the two controller action are the same post method..
You can only used that in form posting scenario for example you are submitting a form data that uses HTTP post..

Resources