I am looking for the optimized way of checking if tempdata has value or not in every controller in MVC. I don't want to write in every controller, Where can I write this condition that if temp data has value then show the view else redirect to Login page.
Please create a base controller
public class BaseController : Controller
{
protected override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
var id = filterContext.Controller.TempData["id"];
if (id == null)
{
filterContext.Result = RedirectToAction("Index", "Home");
}
base.OnActionExecuting(filterContext);
}
}
After that you have to inherit the base controller on very controller.
public class CheckController : BaseController
{
public ActionResult Index()
{
TempData["id"] = 2;
return View();
}
}
Use base controller in every controller. Then check in base controller that if temp data has value then show the view else redirect to Login page.
Filter code:
public class MyCustomAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.TempData.Add("some key", "some value");
}
}
Action code:
[MyCustom]
public ViewResult Index()
{
string value = TempData["key"] as string;
return View();
}
Related
I have a custom auth set up so that a user is stored as a session variable. Once they go through the Account/LogIn process I store the details returned from a 3rd party API as a user in the session like this:
Session["User"] = new UserViewModel(result);
I want to check the user is present before every controller action so I have made a BaseController with the following check in it:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Session["User"] != null)
base.OnActionExecuting(filterContext);
else
filterContext.Result = new RedirectToRouteResult(new System.Web.Routing.RouteValueDictionary(new { action = "LogIn", controller = "Account" }));
Each of the controllers then inherits from the BaseController so that it redirects to the Log In page if there is no user. I don't inherit from the BaseController for the AccountController so that it doesn't get in an infinite loop of checking and redirecting, but I also want to have specific pages not check for log in. Is there any way to do this, i.e. write an exception rule in the same way that you might have [AllowAnonymous]?
You could use a filter on those methods as:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public class ActionCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower().Trim();
string actionName = filterContext.ActionDescriptor.ActionName.ToLower().Trim();
// this is just a sample.. you can implement any logic you want
if (!actionName.StartsWith("your method name") && !controllerName.StartsWith("your controller name"))
{
var session1 = HttpContext.Current.User.Identity.Name;
HttpContext ctx = HttpContext.Current;
//Redirects user to login screen if session has timed out
if (session1 == null)
{
base.OnActionExecuting(filterContext);
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Account",
action = "LogOff"
}));
}
}
}
}
then on the controllers put the attribute as:
[ActionCheck]
public class MyController : Controller
{
public ActionResult Index()
{
return View();
}
}
or on specific action methods as:
[ActionCheck]
public Actionresult SomeMethod()
{
return View();
}
Say I have a controller with multiple actions, is there an override to make the controller return a default action if a condition is met?
Example:
I have a checkoutcontroller and I want each action to return HttpNotFound() if e-commerce is disabled on the site, is there a better way of doing it than just doing the following:
public class CheckoutController
{
[HttpGet]
public ActionResult ViewBasket()
{
if (AppSettings.EcommerceEnabled)
{
return View();
}
else
{
return HttpNotFound();
}
}
[HttpGet]
public ActionResult DeliveryAddress()
{
if (AppSettings.EcommerceEnabled)
{
return View();
}
else
{
return HttpNotFound();
}
}
}
You can create a custom action filter that will be used for action methods inside CheckoutController.
public class CommercePermissionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (AppSettings.EcommerceEnabled)
{
base.OnActionExecuting(filterContext);
}
else
{
// Example for redirection
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{ "Controller", "Error" },
{ "Action", "AccessDenied" }
});
}
}
}
Then you can use this filter on each action method by
[HttpGet]
[CommercePermission]
public ActionResult ViewBasket()
Or if you want whole controller actions to have this filter,
[CommercePermission]
public class CheckoutController
You can even apply filters globally to all actions in your project.
You can find more info here.
You could create a custom action filter. This would intercept the routing to that action, apply its internal logic, and either continue to the action or interrupt it as you define in the filter. Filters can be applied to individual actions, an entire controller class, or even globally for the whole application.
Let's imagine I have the following action
public ViewResult Products(string color)
{...}
and route which maps url "products" to this action.
According to SEO tips link /products?color=red should return
200 OK
But link /products?someOtherParametr=someValue
404 Not found
So the question is - how to handle unexisting query parameters and return 404 in this case
Considering the accepted answer at What is the proper way to send an HTTP 404 response from an ASP.NET MVC action?, there is a special ActionResult that could fulfill your expectation.
public class HomeController : Controller
{
public ViewResult Products(string color)
{
if (color != "something") // it can be replaced with any guarded clause(s)
return new HttpNotFoundResult("The color does not exist.");
...
}
}
Update:
public class HomeController : Controller
{
public ViewResult Products(string color)
{
if (Request.QueryString.Count != 1 ||
Request.QueryString.GetKey(0) != "color")
return new HttpNotFoundResult("the error message");
...
}
}
Validate this before executing the Action Method. This approach is valid for all Controller of you project or specific Action method.
Controller Action Methods
[attr] // Before executing any Action Method, Action Filter will execute to
//check for Valid Query Strings.
public class ActionResultTypesController : Controller
{
[HttpGet]
public ActionResult Index(int Param = 0)
{
return View();
}
[HttpPost]
public ActionResult Index(MyViewModel obj)
{
return View(obj);
}
}
Action Filter
public class attr : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionParameters.Count == 0 &&
System.Web.HttpContext.Current.Request.QueryString.Count > 0)
{
//When no Action Parameter exists and Query String exists.
}
else
{
// Check the Query String Key name and compare it with the Action
// Parameter name
foreach (var item in System.Web.HttpContext
.Current
.Request.QueryString.Keys)
{
if (!filterContext.ActionParameters.Keys.Contains(item))
{
// When the Query String is not matching with the Action
// Parameter
}
}
}
base.OnActionExecuting(filterContext);
}
}
If you pay attention to the above code, we are checking the Action parameter as shown in the screen show below.
What can we do in case the QueryString passed does not exists in the Action Method Parameter? We can redirect the user to another page as shown in this link.
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{"action", "ActionName"},
{"controller", "ControllerName"},
{"area", "Area Name"},
{"Parameter Name","Parameter Value"}
});
Or
We can do like this. The below mentioned code will be written in the OnActionExecuting Method Override
filterContext.Result = new HttpStatusCodeResult(404);
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var actionParameters = filterContext.ActionParameters.Keys;
var queryParameters = filterContext
.RequestContext
.HttpContext
.Request
.QueryString
.Keys;
// if we pass to action any query string parameter which doesn't
// exists in action we should return 404 status code
if(queryParameters.Cast<object>().Any(queryParameter
=> !actionParameters.Contains(queryParameter)))
filterContext.Result = new HttpStatusCodeResult(404);
}
Actually, if you don't want to write this is in every controller, you should override DefaultControllerFactory
This works in Asp.Net Core.
In Controller ActionMethods use below one liner code:
return StatusCode(404, "Not a valid request.");
In OnActionExecuting method, set StatusCode to context.Result as in below sample:
public override void OnActionExecuting(ActionExecutingContext context)
{
string color = HttpContext.Request?.Query["color"].ToString();
if (string.IsNullOrWhiteSpace(color))
{
// invalid or no color param, return 404 status code
context.Result = StatusCode(200, "Not a valid request.");
}
else
{
// write logic for any common functionality
}
base.OnActionExecuting(context);
}
Is it posible to set the layout to null in action filter? For example
public ActionResult SomeAction()
{
return PartialView();
}
I want to render some action with
#Html.Action("someaction")
it works for now.
But i want to use this action in 2 modes : like child and like master for different situations. I can set Layout to null in view for this
view:
#{
if(condtition)
{
Layout = null;
}
}
But i want to find more elegant way :)
Like:
action without partial
public ActionResult SomeAction()
{
return View();
}
and in filter set the layout to null if action is child
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.IsChildAction)
{
//set the layout to NULL here!
}
}
}
It is posible? Any ideas?
In your example you have overriden the OnActionExecuting event but this happens way too early. The actions hasn't even executed yet nor returned an ActionResult and you are already attempting to set its Layout.
Wait for it to complete, by overriding the OnActionExecuted event, retrieve the Result property from the filterContext and if it is a ViewResult set its MasterName property to null:
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = null;
}
}
}
is there any way to assign an entire controller to use a certain layout? I know you can assign the layout in the ViewStart, is there anyway for the viewStart to know what controller is being used?
My objective is to have two admin layouts, one with an extra navbar when you are working with anything in the admin controller.
You could write a custom action filter:
public class LayoutAttribute : ActionFilterAttribute
{
private readonly string _layout;
public LayoutAttribute(string layout)
{
_layout = layout;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = _layout;
}
}
}
and then decorate your controller with it and all actions (returning view results obviously) withing this controller will use the layout you have specified:
[Layout("_SimpleLayout")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}