I want to get ViewData value from httpcontext.
My function:
[LogActionFilter]
public ActionResult Edit(int id = 0)
{
var obj = getObjFromDb(id);
ViewData["abc"] = obj.name;
return View(obj);
}
My action filter where I want to to access ViewData value:
public class LogActionFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var a= filterContext.HttpContext.Items["abc"]; //null
var b = filterContext.HttpContext.Request.RequestContext.HttpContext.Items["abc"]; //null
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var a= filterContext.HttpContext.Items["abc"]; //null
var b = filterContext.HttpContext.Request.RequestContext.HttpContext.Items["abc"]; //null
}
}
How can I access the value of ViewData from HttpContext?
You can use session to pass value into your OnActionExecuted filter. However you can't pass anything from your action to OnActionExecution because its executed before your action.
[LogActionFilter]
public ActionResult Edit(int id = 0)
{
var obj = getObjFromDb(id);
Session["abc"] = obj.name;
return View(obj);
}
In filter:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var a = filterContext.HttpContext.Session["abc"];
/// a should have a value
}
Thanks to Stephen Muecke,
ViewData is not available in HttpContext
So I changed my logic, instead of getting viewdata value, I fetched data from database in action filter.
Related
I am having some problems implementing a shopping cart into my ASP.NET Core application. I am using session storage to do this but every time OnActionExecuted executes, the cart object passed to the filter is empty. Anyone have any idea why?
Session filter class:
public class WinkelmandSessionFilter : ActionFilterAttribute
{
private Winkelmand _mand;
public WinkelmandSessionFilter()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
_mand = ReadCartFromSession(context.HttpContext);
context.ActionArguments["cart"] = _mand;
base.OnActionExecuting(context);
}
public override void OnActionExecuted(ActionExecutedContext context)
{
WriteCartToSession(_mand, context.HttpContext);
base.OnActionExecuted(context);
}
private Winkelmand ReadCartFromSession(HttpContext context)
{
Winkelmand cart = context.Session.GetString("cart") == null ?
new Winkelmand() : JsonConvert.DeserializeObject<Winkelmand>(context.Session.GetString("cart"));
return cart;
}
private void WriteCartToSession(Winkelmand cart, HttpContext context)
{
context.Session.SetString("cart", JsonConvert.SerializeObject(cart));
}
}
The method that uses this filter:
[ServiceFilter(typeof(WinkelmandSessionFilter))]
public IActionResult BonEdit(Winkelmand mand, NieuwViewModel model)
{
var bon = new Bon();
bon.NaamGeadreseerde = model.naamGeadreseerde;
bon.EmailGeadreseerde = model.emailGeadreseerde;
bon.NaamGever = model.naamGever;
bon.Bedrag = model.Bedrag;
bon.Boodschap = model.Boodschap;
bon.Winkel = model.Winkel;
bon.BonId = Guid.NewGuid().GetHashCode();
mand.bonToevoegen(bon);
bon.genereerPdf();
return RedirectToAction(nameof(BonVoorbeeld), bon);
}
You are passing the cart to the action using the name cart:
context.ActionArguments["cart"] = _mand;
But when you are accessing it from the action method, the name is mand:
public IActionResult BonEdit(Winkelmand mand, NieuwViewModel model)
In order for it to be passed into the action method, these 2 names must match.
context.ActionArguments["mand"] = _mand;
How do I check if an action returned View() or Redirect() in a custom ActionFilter? I need to check this as my ActionFilter populates the ViewBag with extra stuff. But if it's a redirect it is not needed.
Example
Controller Action
[MyActionFilter]
public IActionResult Index()
{
if (ModelState.IsValid())
return View();
else
return Redirect("foo");
}
Action Filter
public class MyActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
// do something before the action executes
}
public void OnActionExecuted(ActionExecutedContext context)
{
if (returned View)
context.Controller.ViewBag.Foo = "Bar";
else
// do other stuff
}
}
I figured it out.
Using an ResultFilter instead gave me access to the returned type. I also had to change from the after action to the before action as changing the result in the after action generally isn't allowed.
public class MyActionFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext context)
{
if (context.Result is ViewResult)
context.Controller.ViewBag.Foo = "Bar";
else
// do other stuff
}
public void OnResultExecuted(ResultExecutedContext context)
{
}
}
ActionExecutedContext has an ActionResult. You could check whether the ActionResult is a ViewResult or RedirectResult/RedirectToRouteResult in your OnActionExecuted.
I want to read the filter attributes of controller in OnActionExecuting method.
for this I have written this code but this empty array.
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var getActionName = filterContext.ActionDescriptor.ActionName;
var getControllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var getUserName = User.Identity.Name;
var getUserRoles = Roles.GetRolesForUser(getUserName);
foreach (var filter in filterContext.ActionDescriptor.GetCustomAttributes(typeof(Roles), false))
{
var desiredValue = filter.ToString();
}
//some business logic here
}
}
this is my controller
[Authorize(Roles = "Admin")]
public class AdminController : BaseController
{
public ActionResult Index()
{
return View();
}
}
I want to get list of allowed roles for executing controller.
You can use GetFilterAttributes method of ActionDescriptor or ControllerDescriptor:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var filters = new List<FilterAttribute>();
filters.AddRange(filterContext.ActionDescriptor.GetFilterAttributes(false));
filters.AddRange(filterContext.ActionDescriptor.ControllerDescriptor.GetFilterAttributes(false));
var roles = filters.OfType<AuthorizeAttribute>().Select(f => f.Roles);
...
}
I have a controller action that has an attribute applied in which if the ModelState has errors it sets them as the JsonResult in the OnResultExecuting method.
I set the value in MyAction. I change it in the attribute in OnResultExecuting but in the OnResultExecuted which is in the controller the result is the one from the controller not the one which was set in the attribute.
So my question is why does the value in OnResultExecuted remain unchanged and how do i make it stop doing that ?
public class MyController:Controller
{
[ValidateDatedObject(SkipActionExecution = true, LeaveJustModelState = true)]
public JsonResult MyAction(ViewModel viewModel)
{
return new JsonResult { Data = new { Success = false }}; // Setting the initial value
}
protected override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);//filterContext.Result here is the on from the controller instead of the one from the attribute
}
}
public class ValidateDatedObject : ModelValidationFilter
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
base.OnResultExecuting(filterContext);
}//filterContext.Result here is the one from the attribute
}
public abstract class ModelValidationFilter : ActionFilterAttribute
{
private JsonResult getModelStateAsJsonResult(ModelStateDictionary modelState)
{
return new JsonResult { Data = new { modelState = SerializeErrors(modelState) } };
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.Result = getModelStateAsJsonResult(filterContext.Controller.ViewData.ModelState); //Setting filterContext.Result here
}
}
That's because in the OnResultExecuting you are replacing the current result with a new instance. That will modify the result in the ResultExecutingContext but will leave the overall result unchanged.
You could however modify the result instead of replacing it
public abstract class ModelValidationFilter : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
//Modify the values in the current filterContext.Result instead of replacing it with a new instance
var jsonResult = filterContext.Result as JsonResult;
if(jsonResult == null) return;
//possibly replace Data only under certain conditions
jsonResult.Data = new { modelState = SerializeErrors(modelState) };
}
}
The reason for this is the way ResultFilters are executed by MVC. You can check the implementation of ControllerActionInvoker.InvokeActionResultFilterRecursive. This is the code calling OnResultExecuting on each filter, executing the action and then calling OnResultExecuted in reverse order.
If you look carefully, you will notice that ResultExecutedContext is created with a reference to the original actionResult object, not with a reference to ResultExecutingContext.Result. (Unless you set ResultExecutingContext.Cancel=true which will stop processing additional filters and return whatever result it has at that moment, but that also means controller OnResultExecute won't be executed)
So there is an assumption in this code in that ResultFilters may modify the values of the properties in ResultExecutingContext.Result but not entirely replace it with a new instance.
Assume I have a controller method like this:
[Audit]
public JsonNetResult List(int start, int limit, string sort, string dir, string searchValue, SecurityInputModel securityData)
{
...
}
and an attribute defined as such:
[AttributeUsage(AttributeTargets.Method)]
public class AuditAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// auditing code here
base.OnActionExecuted(filterContext);
}
}
can I get at the value of start/limit/sort/etc from inside OnActionExecuted()?
You can get the parameter values in OnActionExecuting using the ActionExecutingContext.ActionParameters property.
For example, the following test attribute writes the parameter names and values out to the response (the ItemModel class overrides ToString to just output its 2 properties):
public class CustomActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var response = filterContext.HttpContext.Response;
response.Write(filterContext.ActionDescriptor.ActionName);
response.Write("<br/>");
foreach (var parameter in filterContext.ActionParameters)
{
response.Write(string.Format("{0}: {1}", parameter.Key, parameter.Value));
}
}
}
[CustomActionFilter]
[HttpPost]
public ViewResult Test(ItemModel model)
{
return View(model);
}