ASP.NET MVC - Overriding an action with differing parameters - c#

I have a controller that inherits from a base controller. Both have an edit (post) action which take two arguments:
On Base controller:
[HttpPost]
public virtual ActionResult Edit(IdType id, FormCollection form)
And in the derived controller:
[HttpPost]
public ActionResult Edit(int id, SomeViewModel viewModel)
If I leave it like this I get an exception because there is an ambiguous call. However, I can't use override on the derived action, because the method signatures don't exactly match. Is there anything I can do here?

As addition to Developer Art's answer a workaround would be:
leave the base method as it is and in your derived class implement the base method and annotate it with [NonAction]
[NonAction]
public override ActionResult Edit(IdType id, FormCollection form)
{
// do nothing or throw exception
}
[HttpPost]
public ActionResult Edit(int id, SomeViewModel viewModel)
{
// your implementation
}

I'd chain it:
On Base controller:
[HttpPost]
public virtual ActionResult Edit(IdType id, FormCollection form)
And in the derived controller:
[HttpPost]
public virtual ActionResult Edit(IdType id, FormCollection form)
{
var newId = //some enum? transform
var boundModel = UpdateModel(new SomeViewModel(), form);
return Edit( newId, boundModel );
}
[HttpPost]
public ActionResult Edit(int id, SomeViewModel viewModel)
I haven't tested this, passing a Post method to another Post should work. There could be security implications this way.

That's all what you need to do
On Base controller :
adding virtual keyword
On Derived controller :
adding override keyword

Related

Where to put common code in view controller View functions?

In one of the controllers, every view has a fixed preprocessing. Is there a better way of doing this, instead of the below code; such that SomeFunctionAsync works without writing that line before return View() for all functions with return View() in this controller? I also have some ajax post functions.
public async Task<ActionResult> View1()
{
await SomeFunctionAsync();
return View();
}
public async Task<ActionResult> View2()
{
await SomeFunctionAsync();
return View();
}
In other words, at the end I want to be able to write the following with having the same effect
public async Task<ActionResult> View1()
{
return View();
}
public async Task<ActionResult> View2()
{
return View();
}
If Action Filter as suggested by Varun doesnt suits you, you can try another way.
Create a parent View of all the view. In the action method for your parent view. Call this Method SomeFunctionAsync(). Thus, parent view will be called for all of yours views and the required method will be executed
You can create a base class for your controller, and have have each view in your code call a generic method. I use GetView as a method name (or you could override the existing ones).
Like so:
public class MyControllerBase : Controller {
public Task<ActionResult> GetView() {
yourCommonMethod();
return View();
}
public Task<ActionResult> GetView(string viewName) {
yourCommonMethod();
return View(viewName);
}
public Task<ActionResult> GetView(object model) {
yourCommonMethod();
return View(model);
}
public Task<ActionResult> GetView(string viewName, object model) {
yourCommonMethod();
return View(viewName, model);
}
}
Then in your controller, inherit from that:
public class MyController : MyControllerBase {
public async Task<ActionResult> View1()
{
return GetView();
}
}
If the common method is the same for all controllers and has no controller-specific dependencies, it could be that simple. However, you may want to look at using generics as well:
public class MyControllerBase<T> : Controller {
// base class stuff here based on type T's interface
}
public class MyController : MyControllerBase<MyController> {
// regular class here, sending MyController to the base
}
These are pretty much building blocks of OOP. You may do well to get a book that covers the basics of OOP and work through this type of stuff.
There are tow ways :
Use a single Action with different views like return View("View1") or retrun View("View2"), you can make if else conditions there so you will call your function at a single place.
If you want to go with your current procedure(not recommended) then you have to use Action Filter attribute and decorate it on Controller level then every action would execute your logic before execution of your Action

Use Get And Post in the same Controller

I want to use the HttpGet and HttpPost attributes for one action method. However, I have only seen examples where the attributes are used individually on separate action methods.
For example:
public ActionResult Index()
{
//Code...
return View();
}
[HttpPost]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
I want to have something like this:
[HttpGet][HttpPost]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
I remember having seen this done somewhere, but cannot remember where.
If you really want to do that, you can use the [AcceptVerbs] attribute. (See this SO question)
This way your method can handle the GET and POST verbs (but not others like PUT)
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
If you want your method to handle all verbs, donĀ“t use any attribute at all:
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}

Already defined member called 'Search' with the same parameter types

I'm getting a confusing error and I'm not quite sure why. Usually this kind of error pops up when you have two ActionResults and forget [HttpPost] on one of them. But as you can see, I have [HttpPost] there, so what could be causing this problem?
Error: Type 'PersonalWebsite.Controllers.BlogController' already defines a member called 'Search' with the same parameter types Controllers\BlogController.cs
and the code:
//
// GET: /Blog/Search
public virtual ActionResult Search()
{
return RedirectToAction(MVC.Blog.Index());
}
//
// POST: /Blog/Search
[HttpPost]
[ValidateInput(false)]
public virtual ActionResult Search(SearchViewModel model)
{
// irrelevant code snipped
return View(model);
}
There are no other Search() methods defined in this controller. It's bizarre.
Any ideas?
Your Search method is already defined in another partial.
See here: https://github.com/Imdsm/PersonalWebsite/blob/master/PersonalWebsite/BlogController.generated.cs
[NonAction]
[GeneratedCode("T4MVC", "2.0"), DebuggerNonUserCode]
public virtual System.Web.Mvc.ActionResult Search()
You could create an alias for your method via:
[HttpPost]
[ValidateInput(false)]
[ActionName("Search")]
public virtual ActionResult SearchByPost(SearchViewModel model)
{
// irrelevant code snipped
return View(model);
}

Find specific HttpGet Action in Controller

Consider the following situation. In my controller I have:
public ActionResult Edit(int id)
{
...
}
[HttpPost]
public ActionResult Edit(Model model)
{
...
}
Also I have an ActionFilterAttribute, which applies to some other actions of the same controller. In the OnActionExecuting method I need to get the ActionDescriptor of the HttpGet Edit action:
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// as this is called from the same controller, I use
ActionDescriptor action = filterContext.ActionDescriptor.ControllerDescriptor
.FindAction(filterContext.Controller.ControllerContext, "Edit");
...
}
The problem is, that the FindAction method returns "reference" to the HttpPost Edit action in case of POST requests. How do I make it to look only for HttpGet actions?
You can use maybe attribute ?
public class FooAttribute
{
}
[FooAttribute]
public ActionResult Edit(int id)
{
...
}
you can check OnActionExecution;
example;
var isHasAttribute= filterContext.ActionDescriptor.IsDefined(typeof(FooAttribute), true);

GET and POST methods with the same Action name in the same Controller [duplicate]

This question already has answers here:
MVC [HttpPost/HttpGet] for Action
(4 answers)
Closed 2 years ago.
Why is this incorrect?
{
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
}
How can I have a controlller thas answer one thing when is "getted" and one when is "posted"?
Since you cannot have two methods with the same name and signature you have to use the ActionName attribute:
[HttpGet]
public ActionResult Index()
{
// your code
return View();
}
[HttpPost]
[ActionName("Index")]
public ActionResult IndexPost()
{
// your code
return View();
}
Also see "How a Method Becomes An Action"
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) {...}
I like to accept a form post for my POST actions, even if I don't need it. For me it just feels like the right thing to do as you're supposedly posting something.
public class HomeController : Controller
{
public ActionResult Index()
{
//Code...
return View();
}
[HttpPost]
public ActionResult Index(FormCollection form)
{
//Code...
return View();
}
}
To answer your specific question, you cannot have two methods with the same name and the same arguments in a single class; using the HttpGet and HttpPost attributes doesn't distinguish the methods.
To address this, I'd typically include the view model for the form you're posting:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
Some Code--Some Code---Some Code
return View();
}
[HttpPost]
public ActionResult Index(formViewModel model)
{
do work on model --
return View();
}
}
You received the good answer to this question, but I want to add my two cents. You could use one method and process requests according to request type:
public ActionResult Index()
{
if("GET"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for GET
}
else if("POST"==this.HttpContext.Request.RequestType)
{
Some Code--Some Code---Some Code for POST
}
else
{
//exception
}
return View();
}
Can not multi action same name and same parameter
[HttpGet]
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(int id)
{
return View();
}
althought int id is not used
You can't have multiple actions with the same name. You could add a parameter to one method and that would be valid. For example:
public ActionResult Index(int i)
{
Some Code--Some Code---Some Code
return View();
}
There are a few ways to do to have actions that differ only by request verb. My favorite and, I think, the easiest to implement is to use the AttributeRouting package. Once installed simply add an attribute to your method as follows:
[GET("Resources")]
public ActionResult Index()
{
return View();
}
[POST("Resources")]
public ActionResult Create()
{
return RedirectToAction("Index");
}
In the above example the methods have different names but the action name in both cases is "Resources". The only difference is the request verb.
The package can be installed using NuGet like this:
PM> Install-Package AttributeRouting
If you don't want the dependency on the AttributeRouting packages you could do this by writing a custom action selector attribute.
Today I was checking some resources about the same question and I got an example very interesting.
It is possible to call the same method by GET and POST protocol, but you need to overload the parameters like that:
#using (Ajax.BeginForm("Index", "MyController", ajaxOptions, new { #id = "form-consulta" }))
{
//code
}
The action:
[ActionName("Index")]
public async Task<ActionResult> IndexAsync(MyModel model)
{
//code
}
By default a method without explicit protocol is GET, but in that case there is a declared parameter which allows the method works like a POST.
When GET is executed the parameter does not matter, but when POST is executed the parameter is required on your request.

Categories