How to override an action method in a controller? Can anyone explain with a small example.
And one more thing to ask , can we do this without virtual keyword?
As far as i m understanding your question these are the answers :
First Answer :
it's not possible to have two controller actions with the same name but with a different result also:
For example:
ActionResult YourAction() { ... }
FileContentResult YourAction() { ... }
In MVC you can also do this :
[HttpGet]
[ActionName("AnyAction")]
ActionResult YourAction(firstModel model1) { ... }
[HttpPost]
[ActionName("AnyAction")]
FileContentResult YourAction(secondModel model1) { ... }
The main idea here is that you can use the ActionNameAttribute to name several action methods with the same name.
----------------------------------------------------------------OR--------------------------------------------------------------
Second Answer :
[NonAction]
public override ActionResult YourAction(FormCollection form)
{
// do nothing or throw exception
}
[HttpPost]
public ActionResult YourAction(FormCollection form)
{
// your implementation
}
You can do this the same as how the Filters will hook into it when you use filters in an mvc solution
public override void OnActionExecuting(ActionExecutingContext context)
{
if (Request.Headers.TryGetValue("api-key", out var value))
{
///
}
base.OnActionExecuting(context);
}
Related
I am working with a WEB API application in ASP .NET Core 2.0 where I have a custom filter attribute that inherits from ActionFilterAttribute.
How can I access the Model object passed to a controller action in POST call, in ActionFilterAttribute.OnResultExecuted()
The mentioned method is passed a ResultExecutedContext object but I could not find an easy and reliable way to get Model object from it.
The filter I have, looks like the following:
public sealed class MyCustomFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnResultExecuted(ResultExecutedContext context)
{
var model = ?????????????
}
public override void OnResultExecuting(ResultExecutingContext context)
{
base.OnResultExecuting(context);
}
}
and the controller looks like the following:
[Route("api/[controller]")]
public class MyController : Controller
{
[ServiceFilter(typeof(MyCustomFilter))]
[HttpPost]
public async Task<IActionResult> Post([FromBody] List<MyModel> data)
{
// my logic to process model here. In my filter, I want to access data, which is passed into this Post method
return Ok();
}
}
I set a private var in the controller and assign the posted model to it on the controllers action
private YourModel _yourModel;
public ActionResult MyAction(YourModel model)
{
_yourModel = model;
return View();
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
//Access _yourModel here
}
To access the model from the filterContext parameter passed in, you can use the below
var model = ((Controller)filterContext.Controller).ViewData.Model;
Try using TempData. Set tempdata to the model in your action.
TempData["Model"] = myModel;
Then access using the TempData on the context.
var x = ((Controller)filterContext.Controller).TempData["Model"];
Hope that's what you meant.
thanks
I got a controller in my mvc application like below.
public class BaseController: Controller
{
protected void LogInfo()
{
logger.InfoFormat("[SessionID: {0}, RemoteIP: {1}]", Session.SessionID, Request.UserHostAddress); }
}
public class FirstController : BaseController
{
public ActionResult Index(string name)
{
LogInfo();
getQueryString();
if(IsValidRec())
{
if(Errors()))
{
return View("Error");
}
var viewname = getViewName(name);
return view(viewname);
}
else
return view("NotFound");
}
}
I need to create another controller(SecondController ) with same ActionResult method that FirstController has but without any implementation. Because I dont wont to repeat same code in 2 ActionResult methods.
what is the best way to do that. I tried in following way but I m getting error when I initialize my protected method 'LogInfo()'
public class SecondController : BaseController
{
public ActionResult Index(string name)
{
var firstcontroller = new FirstController();
return firstcontroller.Index(name);
}
}
Put the part you want to re-use in the base controller
e.g.
public class BaseController: Controller
{
protected void LogInfo()
{ ... }
virtual public ActionResult Index(string name)
{
LogInfo();
getQueryString();
.....
var viewname = getViewName(name);
return view(viewname);
}
}
public class FirstController : BaseController
{
override public ActionResult Index(string name)
{
var result = base.Index(name);
.... do more stuff ...
return result;
}
}
public class SecondController : BaseController
{
// Don't need to override index if you
// want to do the same as in the base controller
}
You can use inheritance like this (one way):
public abstract class MyControllerBase : Controller
{
// whatever parameters
protected SharedModel GetSharedModel()
{
// do logic
// return model
}
}
public class OneController : MyControllerBase
{
protected ActionResult Index()
{
var model = this.GetSharedModel()
return this.View(model);
}
}
public class TwoController : MyControllerBase
{
protected ActionResult Index()
{
var model = this.GetSharedModel()
return this.View(model);
}
}
It is better to put common functionality somewhere else in application and use that in both controller. Like we used to write helper classes or use shared services. It is not good to create the instance of controller and calling the Action method from that...from the Architectural point of view. If u still have doubt...please explain more about your common functionality...then I will be able to help more.
There are two solutions to this type of issue: inheritance and composition.
In general, inheritance has less code, but is less flexible.
Here's a more through discussion.
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);
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.
How to enable Authentication on whole controller and disable only for certain action methods. I want authentication for all resources. If I write something like that:
[Authorize]
public class HomeController : BaseController
{
//This is public
[UnAuthorized]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}
Then anyone can access this resource. I need this because we have all resources are private and very few are public on our project. Do you have any ideas how to make it better way?
Organize your controllers accordingly. Have a base controller for all authenticated resources which you could annotate with the [Authorize] attribute and another one for public resources.
[Authorize]
public abstract BaseAuthenticatedController : Controller
{ }
public abstract BaseController : Controller
{ }
Based on solution which is found here I wrote the code that fixes exactly what I wanted.
Create custom authorization attribute base on AuthorizeAttribute and override method OnAuthorization:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext != null)
{
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(false);
if (attributes != null)
{
foreach (var attribute in attributes)
if (attribute is UnAuthorizedAttribute)
return;
}
}
base.OnAuthorization(filterContext);
}
I'm using a reflection here to recognize an action with UnAuthorized attribute. I don't know about performance issues in this case, but it solves the problem completely.
It's really strange that no one said about AllowAnonymous attribute which services for such situations:
[Authorize]
public class HomeController : BaseController
{
//This is public
[AllowAnonymous]
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
//This is private resource
public ActionResult PrivateResource()
{
return View();
}
}