I need to create a general handler for all ASP.NET MVC queries. Now I use Application_BeginRequest but there come not only ASP.NET MVC queries.
The one option I see is to create a common base controller and execute my logic in its constructor. But may be there are other options?
Have you considered action filters?
You have to add ActionFilter to your controller.
In order to do this you have to create an class inherited from ActionFilterAttribute
For example:
Public Class CustomFilterAttribute
Inherits ActionFilterAttribute
End Class
Than just apply this attribute to controller:
<CustomFilter()> _
Public Class MyController
There are 4 methods in ActionFilterAttribute which can be overrided:
OnActionExecuted
OnActionExecuting
OnResultExecuted
OnResultExecuting
Override them and this code will be executed on each request to methods of your controller
idsa,
you might be able to rustle something up using this kind of approach in a base controller:
protected override void Initialize(RequestContext requestContext)
{
Lang = requestContext.RouteData.Values["lang"].ToString()
?? System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
ViewData["Lang"] = Lang;
base.Initialize(requestContext);
// your custom logic here...
}
or on:
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
base.Execute(requestContext);
// intercepting code here...
}
or:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// one stage later intercepting code here
}
who knows mind you :)
Related
Hi guys I’m working on an Asp.net core project targeted .Net 5
I created a class inherited from IActionFilter and I used the OnActionExecution method I did some logic in it and wanna redirect from it to another Action in a Controller.
The problem :
The problem is how can I redirect to the action that request came from and I tried many solution depending on my knowledge but no one succeeds.
What I tried :
public class ValidationFilterAttribute : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//some logic to get the model
if(!context.ModelState.IsValid)
{
Context.Result = new RedirectToAction(“Idon’t know how to get action”, “I don’t know how to get controller”,new{model= /*model I got*/});
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
Question :
I have two questions,
How to know the Action and the Controller names that request came from ?
How can I redirect to the same Action but in the same Controller and send the same model gotten in ‘OnActionExecution’
Why I do that :
My Idea is using my own ‘IActionFilter’ class with any method worked in HttpPost To check if the model sent is valid or not and if not valid On OnActionExecution method will add errors to the model and resent it again to the action.
Please any help about this issue ?
You can define a custom ActionFilter like this
public class ValidationFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; // Get controller name
var modelState = (filterContext.Controller. as Controller).ViewData.ModelState; // Get the model state of the request
var model = (filterContext.Controller as Controller).ViewData.Model; // Get the model of the request
//do your stuff here
base.OnActionExecuting(filterContext);
}
}
And then register your filter so it will be applied to every controller and every action in your project
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore(options =>
{
options.Filters.Add(typeof(ValidationFilterAttribute));
});
}
This way before hitting the controller's action method body each request will pass through your ValidationFilterAttribute where you can examine his Model and change his ModelState.
Let's say I have a Currency cookie, it should be either USD or GBP. If someone manually change it to for example RUB, it will revert back to USD.
To achieve this, I created BaseController class to inherits System.Web.MVC.Controller, then every controller in my project will inherits BaseController, inside BaseController constructor I call CheckcookieValidity() method. Code below :
public class BaseController : Controller
{
public BaseController()
{
If (CheckCookieValidity() == false)
{
SetDefaultCookie();
}
}
}
public void SetDefaultCookies()
{
var curr = new HttpCookie("curr");
curr.Value = "USD";
curr.Expires = DateTime.UtcNow.AddDays(2);
HttpContext.Current.Response.Cookies.Set(curr);
}
I have some problem with this, if SetDefaultCookie() is called, the cookie doesn't change until the next page so
Is it possible to redirect/refresh page inside constructor?
Is this an acceptable way to check cookie validity? I know this question can be categorized as opinionated, but I need to know if there's a better way to achieve this.
Any help will be appreciated and apologize for bad english.
ANSWER :
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (CheckCookiesValidity() == false) {
SetDefaultCookies();
RouteValueDictionary route = new RouteValueDictionary(new {
Controller = "Home",
Action = "Index"
});
filterContext.Result = new RedirectToRouteResult(route);
return;
}
}
I don't know if its possible to redirect inside a Controller's constructor, but that certainly seems like a bad idea.
If you want to keep going the route of checking each request before the action, then you might want to look at custom Action Filters. There's a bunch of references to them on the web, but here are a couple.
The constructor may not be the best spot for this logic. I personally use the Initialize method to my custom AuthUser loading:
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
// Custom code logic for every controller here
}
Expanding on Tim's suggestion, an attribute may also be recommended. Something like this comes to mind:
public class CurrencyCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
// Put your logic and potential redirect here.
}
}
This can then be applied globally to all of your controllers as such:
[CurrencyCheck()]
public class BaseController : Controller
Hope this helps you out.
I downloaded a sample MVC application and in it there is a class called "AuthorizationHelper" that inherits from ActionFilterAttribute but doing a search of the project I can not find where or how this helper is being called so I am confused on this is being called. I understand what the helper is doing I just can't figure out how it might be called. Here is the code if it helps at all:
public class AuthorizationHelper : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
string AppLocation = WebConfigurationManager.AppSettings["AppLocation"];
if (AppLocation == "Development")
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Home"},
{"action", "Index"},
{"area", ""}
}
);
}
base.OnActionExecuted(filterContext);
}
}
This is an action filter which can either be registered in App_Start\FilterConfig.cs, or used to decorate controllers, or actions. If you take a look in App_Start\FilterConfig.cs, you might find something like this:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new AuthorizationHelper());
}
}
In your particular case, OnActionExecuted means the filter will be executed after an action has been executed but before the action result has been executed.
For more information, I'd suggest reading Understanding Action Filters (C#). Although this link is for older versions of MVC, the only difference is that of how the filter is registered. Versions of MVC older than MVC 4 would register filters directly within Global.asax, rather than in App_Start\FilterConfig.cs.
I have a multitenacy WebSite, and i need to solve the routing so that:
A request enters to
www.maindomain.com/{site}/{controller}/{action}/{id}
First, a global filter is executed, and stores the current site on ViewData
ViewData["tenant"] = current {controller} request
And then, the correct controller/action is executed, so that it can retrive the request's site:
public class ViewItem : Controller
{
public ActionResult ViewItem(int id)
{
string site = ViewData["tenant"];
return View("you are viewing the item" + id + " from tenant" + tenant ;
}
}
I think i need to configure some routing, is it possible?
Create a custom ActionFilter. The trick is to hook into OnActionExecuting method, which is called before the action method in controller.
public class PropagateTenantToViewDataFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["tenant"] =
filterContext.RouteData.Values["tenant"];
}
}
Register this filter as a global filter for all controllers and actions - add this line to your Application_Start method in Global.asax.cs file:
GlobalFilters.Filters.Add(new PropagateTenantToViewDataFilter());
If you are using App_Start and FilterConfig, then add the following line to your RegisterGlobalFilters instead:
filters.Add(new PropagateTenantToViewDataFilter());
See e.g. this page for more information about Action Filters in ASP.NET MVC.
I'd look at Autofac mutli tennancy it made our lives a lot easier. Can define a tenant identification strategy and use that to make DI decisions allowing tenant specific services or controllers etc.
Is it possible to make an action filter or something that runs before the action method itself runs on the controller?
I need this to analyze a few values in the request before the action runs.
You can override OnActionExecuting method in controller class
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
//Your logic is here...
}
You could use an attribute:
public class MyFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Your logic here...
base.OnActionExecuting(filterContext);
}
}
And if you want to apply it to all controllers, in your Global.asax.cs:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyFilterAttribute());
}
protected void Application_Start()
{
// Other code removed for clarity of this example...
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
// Other code removed for clarity of this example...
}
If u don't want to use a base controller u can also add a own HttpHandler and register it in the web.config. In the BeginProcessRequest method u can analyse the values.