ASP.NET MVC 4, how to access/modify the view model object (and change view and action method) before it is used as action method parameter?

最后都变了- 提交于 2019-12-05 12:58:20

You could use an action filter and override the OnActionExecuting event:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        ...            
    }
}

Now let's see what useful information you could extract from this filterContext argument that is passed to this method. The property you should be looking for is called ActionParameters and represents an IDictionary<string, object>. As its name suggests this property contains all the parameters that are passed to the controller action by name and value.

So let's suppose that you have the following controller action:

[MyActionFilter]
public ActionResult Index(MyViewModel model)
{
    ...
}

Here's how you could retrieve the value of the view model after model binding:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var model = filterContext.ActionParameters["model"] as MyViewModel;
        // do something with the model
        // You could change some of its properties here
    }
}

Now let's see the second part of your question. How to shortcircuit the controller action and redirect to another action?

This could be done by assigning a value to the Result property:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    {
        ... some processing here and you decide to redirect:

        var routeValues = new RouteValueDictionary(new
        {
            controller = "somecontroller",
            action = "someaction"
        });
        filterContext.Result = new RedirectToRouteResult(routeValues);
    }
}

or for example you decide to shortcircuit the execution of the controller action and directly render a view:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var viewResult = new ViewResult
        {
            ViewName = "~/Views/FooBar/Baz.cshtml",
        };
        MyViewModel someModel = ... get the model you want to pass to the view
        viewResult.ViewData.Model = model;
        filterContext.Result = viewResult;
    }
}

or you might decide to render a JSON result:

public class MyActionFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        MyViewModel someModel = ... get the model you want to pass to the view
        filterContext.Result = new JsonResult
        {
            Data = model,
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}

So as you can see the possibilities are unlimited of what you can do.

user310457

I have experimented with the code in the answer provided by the user Darin Dimitrov, and the first and third parts of the answer are correct.

(Though, for others who might find this thread and be interested, I can clarify that in the first answer the "model" does not seem to be a hardcoded keyword always used for the model but seems to have to correspond to the chosen name of the action method parameter. In other words, if you instead have the method signature

public ActionResult Index(MyViewModel myViewModel)

then in your action filter you have to use

var model = filterContext.ActionParameters["myViewModel"] as MyViewModel;

)

Regarding the second answer, the usage of 'RedirectToRouteResult' will trigger a new http request (which was not desired as I mentioned in the second code example of mine). I found another way of "changing" action method by actually invoking it explicitly:

var controller = new SomeController();
ActionResult result = controller.SomeAction(model);
filterContext.Result = result;

The above code actually seems to prevent the originally targeted action method from becoming invoked, i.e. when I put a breakpoint in the method annotated with '[MyActionFilter]' the execution never got into that method. Typically, it is probably not desired to hardcode a controller like above, but instead reflection might be used, for example as below with the thirdpart library "fasterflect":

string nameOfController = ... 
string nameOfActionMethod = ... 
// both above variables might for example be derived by using some naming convention   and parsing the refering url, depending on what you want to do ...
var theController = this.GetType().Assembly.CreateInstance(nameOfController);
ActionResult result = (ActionResult)theController.CallMethod(nameOfActionMethod, model);
filterContext.Result = result;

(for those who want to extract the names of the current target controller and action method, when implementing logic to determine the controller you want to invoke, you can use this code in the filter:

var routeValueDictionary = filterContext.RouteData.Values;
string nameOfTargetedController = routeValueDictionary["controller"].ToString();
string nameOfTargetedActionMethod = routeValueDictionary["action"].ToString();

)

I think it feels a bit awkward to instantiate and invoke controllers like above, and would prefer to change the target controller and action method in another way if possible ? So, the remaining question is if there is still (in MVC 4 final version) no way of redirecting/forwarding execution "internally" (without a new http request being fired as with 'RedirectToAction') at the server ?

Basically, I think I am here just looking for something like "Server.Transfer" which was used with ASP.NET Web Forms (and also the old classic ASP I believe could use the same thing).

I have seen older question/answers on this issue with people implementing this behaviour themselves with some "TransferResult" class of their own, but it seems to tend to become broken i different MVC versions. (for example, see here for MVC 4 beta: How to redirect MVC action without returning 301? (using MVC 4 beta) ).

Is there really still not a simple standard solution (implemented in MVC 4 final) about how to do an "internal redirect" without a new http request (as RedirectToAction does) ?

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!