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.
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.
I am using SelfHost/Katana/Owin for my WebServer.
I have a Controller in there that I want to enable/disable by code depending on a command line argument at launch time.
Is there a simple way of doing this in MVC?
Right now I'm thinking in the Controller's code to return HTTP-NotFound status code when this config is disabled, any better ideas?
You could decorate your controller with a custom Action Filter.
public class ConfigActionFilter : ActionFilterAttribute {
// This method is called before a controller action is executed.
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if(someConfigSetting) {
filterContext.Result = new RedirectToRouteResult("Error", someRouteValues);
}
}
...
}
Usage:
[ConfigActionFilter]
public class MyController : Controller {
...
}
More here.
You could perform a redirecttoaction that will take users to a different controller explaining what's happening.
ie:
public class MyController : Controller {
private IConfigReader _configReader;
public MyController(IConfigReader configReader){ //not sure if you're doing dependency injection or not, so I'm injecting it
_configReader = configReader;
}
public ActionResult Index() {
if(!_configReader.IsEnabled) {
return RedirectToAction("Denied", "AuthController");
}
//etc
return View();
}
}
You could create an attribute, apply it to the controller and set a static property on that attribute at startup time, and deny access (or return "Not found") when the flag is set.
Alternatively, you can implement a custom AuthorizationAttribute and put it on your controller
public class AuthorizationAdminAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (/*check for argument*/)
{
return false;
}
return true;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (AuthorizeCore(filterContext.HttpContext))
{
// ** IMPORTANT **
// Since we're performing authorization at the action level, the authorization code runs
// after the output caching module. In the worst case this could allow an authorized user
// to cause the page to be cached, then an unauthorized user would later be served the
// cached page. We work around this by telling proxies not to cache the sensitive page,
// then we hook our custom authorization code into the caching mechanism so that we have
// the final say on whether a page should be served from the cache.
HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null /* data */);
}
else
{
filterContext.Result = new HttpNotFoundResult();
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
I work with asp.net c# mvc framework. I need a way to 'turn-off' my web app for all users except administrator (i. e. all pages should return to something like "The application is closed" for all the roles except Admin).
I already create a button in order to save the status of the web app (ON/OFF) in a DB.
Do I have to check on each page the status of the application ?
Is-it possible to have a global redirection except for one role ?
I don't know how to properly do this global closure. Any suggestions are welcomed.
I can think of three approaches to check and do a redircet
An HttpModule hooked into the appropriate, post-authorisation event. Presumably PostAuthorizeRequest of HttpApplication.
In your "global" (Global.aspx.cs) subscribe to that same event.
An MVC Action filter, overriding OnActionExecuting. (Ensure you make it global, to avoid needing to apply to every controller: add to GlobalFilters.Filters in your Application_Start.)
Of these 3 is part of MVC, but is much later in the pipeline (much more work will have been done, to be thrown away when the filter fails).
Use of a module is controlled by configuration which would make is easier to switch on and off.
option 2 is likely easiest to implement, but I would tend to prefer the modularity that 1 gives.
You can accomplish your requirement with the help of custom filters shown below :-
[CheckUserRole]
public class YourController : Controller
{
public ActionResult YourAction()
{
}
}
public class CheckUserRoleAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Get the User Id from the session
// Get Role associated with the user (probably from database)
// Get the permission associated with the role (like Read, write etc)
// if user is not authenticated then do as :
filterContext.Result = new RedirectToRouteResult(new
RouteValueDictionary(new { controller = "Error", action = "AccessDenied" }));
}
}
Did you tryActionFilterAttribute ?
Here is a basic example:
Your controller:
[IsAdmin]
public class YourController
{
}
Your attribute
public class IsAdminAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if () // Check that your user is not an Admin and that your application is "turn-off"
{
filterContext.Result = new HttpStatusCodeResult(403); // or whatever you want
}
}
}
Add [IsAdmin] on top of all your controllers.
You can write in all other Controllers which are used as follows..
public class HomeController : Controller
{
public ActionResult Index()
{
if (User.IsInRole("Administrator"))
return RedirectToAction("PagetoRedirect");
else
return RedirectToAction("CommonPagetoShowApplicationAsClosed");
}
}
Or
Action Filter, you can create on your own and look for named action like IndexRolename
I want to be able to mark an action on controller to be called both from ajax calls and via RenderAction. The problem is that both this attributes derive or implement different abstractions. One way out is the next:
[AjaxOnly]
PartialViewResult GetViewAjax(int foo) { return GetView(foo); }
[ChildActionOnly]
PartialViewResult GetView(int foo) { ... }
But this is not neat at all.
The AjaxOnly attribute I am talking about is:
public sealed class AjaxOnlyAttribute : ActionFilterAttribute
{
#region Public members
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.HttpContext.Request.Headers["X-Requested-With"] != "XMLHttpRequest")
filterContext.Result = new HttpNotFoundResult();
}
#endregion
}
This method is taken from MVC3 futures. An important comment why the condition is not filterContext.HttpContext.Request.IsAjaxRequest() was made by dev team and says the following:
// Dev10 #939671 - If this attribute is going to say AJAX *only*, then we need to check the header
// specifically, as otherwise clients can modify the form or query string to contain the name/value
// pair we're looking for.
This doesn't make any sense. Those 2 attributes are mutually exclusive. If an action is marked with [ChildActionOnly] it can never be directly accessed by the client using an HTTP request (be it synchronous or asynchronous). So if you want an action to ever be accessible using AJAX, you should never decorate it with the [ChildActionOnly] attribute.
I don't know what this [AjaxOnly] attribute is and where it comes from but depending on how it is implemented you might need to tweak it in order to allow child action requests if it relies only on the Request.IsAjaxRequest() method. For example if it is something like this:
public class AjaxOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new HttpNotFoundResult();
}
}
}
you might want to tweak it like this:
public class AjaxOrChildActionOnlyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest() &&
!filterContext.IsChildAction
)
{
filterContext.Result = new HttpNotFoundResult();
}
}
}
Inspired on Darin's answer and ChildActionOnlyAttribute's source code, this is the solution I have come up with, which I think it's a tiny bit better:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class AjaxOrChildAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
return controllerContext.IsChildAction || controllerContext.RequestContext.HttpContext.Request.IsAjaxRequest();
}
}
This way, the validation is done before even trying to execute, and the error you get if you type in the url is the exact same one as trying any invalid url.
I want to be able to, within my Initialize function of my controller, decide WHICH function gets called. So for example if the route dictates that Controller:MyFunction should get called, I want to override that from within the Initialize function and call MyFunction2 for example. How can I achieve this?
To get a better understanding of why I need this here is my current scenario.
I have a Customer Controller. Customers have lists of products. If a customer has a certain product I want to be able to add extra navigation elements and handle new functions.
So in pseudo code here is how I'm thinking it'd work
Receive request
Check if request matches any specialisation classes we have
If (match) then call SpecialClass::SpecificRequestFunction
This way I can have a standard customer controller that deals with all customer things but if I need specialisation for those products I can keep on using the customer controller with all the benefits of re-using that code but can add specialities to it.
I hope this makes sense.
There are a few ways you can do this, you can:
1.Override the OnActionExecuting method of the controller:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (filterContext.ActionDescriptor.ActionName == "About")
filterContext.Result = RedirectToAction("Index");
}
2.Create an ActionFilter (essentially the same as above, and override the OnActionExecuting method there also.
3.Create a route constraint where you can do conditional checks before the request gets to the appropriate controller:
public class IsLocalRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase context, Route route, string paramName, RouteValueDictionary dict, RouteDirection direction
{
return context.Request.IsLocal;
}
}
For your specified piece of work though, it might be good to abstract your navigational items out, e.g.
public interface INavigationalItem
{
string Name { get; }
string Controller { get; }
string Action { get;}
object Parameters { get; }
}
That way you could provide a IEnumerable<INavigationalItem> which you build based on the current user state. You wouldn't have to specialise any additional controllers that way, you build the available navigational items dynamically instead.
If those functions are controller actions you can get the action that is going to be called from the routes:
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
string action = requestContext.RouteData.GetRequiredString("action");
}
but you cannot override it because the request has already been made to the first action. Also why calling this controller action in the first place when you know that you need another action?