MVC 模型在 OnExecuted 操作过滤器中为 null ...或者设置模型的更优雅的方式?

发布于 2024-11-09 20:03:12 字数 1141 浏览 3 评论 0原文

我有一个 ActionFilter,它覆盖了 OnActionExecuted 方法。在 POST 操作中,filterContext.Controller.ViewData.Model 始终为 null。我确实发现下面的文章似乎在说它不应该为 null,但这肯定是 MVC 的早期版本。这就是MVC3。我应该得到什么?

ActionFilter 内的模型可用性

更新:

我已经找到了原始问题的答案。我有一个自定义 ActionResult,它使用自定义日期格式化程序输出 JSON。问题是模型没有在控制器中设置。

在我的自定义 ActionResult 中,ExecuteResult 方法传递给 ControllerContext,如果我可以在那里设置模型,那就太好了:

context.Controller.ViewData.Model = _data;

但这已经是循环的后期,并且 ActionFilter 中的结果仍然为 null。这似乎意味着我需要在控制器中手动设置模型:

ControllerContext.Controller.ViewData.Model = model; 

或者

View(model);

这意味着我需要记住每次使用此自定义 ActionResult 时都执行此操作。有更优雅的方式吗?

另一个更新:

我找到了一种方法来做到这一点,但它并不像我希望的那么优雅。

在我在控制器中发送的comstom ActionResult 的构造函数中,这样至少它将始终保持一致:

public JsonNetResult(object data, Controller controller) {
    SerializerSettings = new JsonSerializerSettings();
    _data = data;
    controller.ControllerContext.Controller.ViewData.Model = _data;
}

I have an ActionFilter with an override on the OnActionExecuted method. The filterContext.Controller.ViewData.Model is always null on a POST operation. I did find the following article that seems to be saying that it should not be null but this must have been an earlier version of MVC. This is MVC3. What should I be getting?

Model availability inside ActionFilter

UPDATE:

I've figured out the answer to the original question. I had a custom ActionResult that outputs JSON with a custom date formatter. The problem was that the model is not being set in the controller.

In my custom ActionResult the ExecuteResult method get passed the ControllerContext which would be nice if I could set the Model there:

context.Controller.ViewData.Model = _data;

But this is to late in the cycle and the result is still null in the ActionFilter. This seems to mean that I need to manually set the model in the controller:

ControllerContext.Controller.ViewData.Model = model; 

Or

View(model);

Which then means I need to remember to do this every time I use this custom ActionResult. Is there a more elegant way?

YET ANOTHER UPDATE:

I found a way to do this it just isn't as elegant as I hoped.

In my constructor for the comstom ActionResult I sending in the controller, that way at least it will alway be consistent:

public JsonNetResult(object data, Controller controller) {
    SerializerSettings = new JsonSerializerSettings();
    _data = data;
    controller.ControllerContext.Controller.ViewData.Model = _data;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时间海 2024-11-16 20:03:12

另一种方法是使用基本控制器自动处理操作参数集合的存储以供以后使用:

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.HttpContext.Items["ActionParms"] = filterContext.ActionParameters.ToDictionary(p => p.Key, p => p.Value);
        base.OnActionExecuting(filterContext);
    }
}

然后在您的属性中:

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    var dictionary = filterContext.HttpContext.Items["ActionParms"] as Dictionary<string, object>;
    if (dictionary != null)
    {
        foreach (var o in dictionary.Keys)
        {
            // do something here
        }   
    }            
    base.OnActionExecuted(filterContext);
}

它使用 HttpContext 项目,这不是很好,但我不知道您可以访问 ViewBag 或 ViewData在属性中。

为了决定是否要在属性中处理请求,您可以询问操作名称和其他参数信息:

var action = filterContext.ActionDescriptor.ActionName;
var parms = filterContext.ActionDescriptor.GetParameters();
foreach (var parameterDescriptor in parms)
{
    // do something here
}

Another approach is to use a base controller to automatically handle the storing of the action parameters collection for later use:

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        filterContext.HttpContext.Items["ActionParms"] = filterContext.ActionParameters.ToDictionary(p => p.Key, p => p.Value);
        base.OnActionExecuting(filterContext);
    }
}

then in your attribute:

public override void OnActionExecuted(ActionExecutedContext filterContext)
{
    var dictionary = filterContext.HttpContext.Items["ActionParms"] as Dictionary<string, object>;
    if (dictionary != null)
    {
        foreach (var o in dictionary.Keys)
        {
            // do something here
        }   
    }            
    base.OnActionExecuted(filterContext);
}

It uses HttpContext items which is not very nice but I don't know that you can access your ViewBag or ViewData in the attribute.

In order to decide whether you want to handle the request in your attribute, you can interrogate the action name and other parameter information:

var action = filterContext.ActionDescriptor.ActionName;
var parms = filterContext.ActionDescriptor.GetParameters();
foreach (var parameterDescriptor in parms)
{
    // do something here
}
你的心境我的脸 2024-11-16 20:03:12

我找到了像您一样的解决方案,之前使用 OnModelUpdated 事件来设置该属性。

我有 ModelBinder:

public class CustomModelBinder: DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        controllerContext.Controller.ViewData.Model = bindingContext.Model;
        base.OnModelUpdated(controllerContext, bindingContext);
    }
}

之后,您需要在 Global.asax 的 Application_Start() 部分将默认绑定器设置为新模型绑定器:

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

最后你可以在 ActionFilter 中访问你的模型:

公共类 TraceLog : ActionFilterAttribute
{
    公共覆盖无效OnActionExecuted(ActionExecutedContext filterContext)
    {
        //filterContext.Controller.ViewData.Model 现在不为 null
        base.OnActionExecuted(filterContext);
    }
}

I found a solution like yours using the OnModelUpdated event to set that property before.

I have the ModelBinder:

public class CustomModelBinder: DefaultModelBinder
{
    protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        controllerContext.Controller.ViewData.Model = bindingContext.Model;
        base.OnModelUpdated(controllerContext, bindingContext);
    }
}

After that, you need to set the default binder to your new model binder in Application_Start() section in Global.asax:

ModelBinders.Binders.DefaultBinder = new CustomModelBinder();

Finally you can access your Model in an ActionFilter:

public class TraceLog : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        //filterContext.Controller.ViewData.Model now isn't null
        base.OnActionExecuted(filterContext);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文