Execute multiple controller actions in one call with MVC.Net 5

十年热恋 提交于 2019-12-24 10:59:08

问题


We have recently upgraded our code base from .Net 4.0 to .Net 4.5.1 and from MVC 2.0 to MVC 5.2.2.

We have a custom method in our base controller class which allowed us to update multiple parts of our views within a single request. Since upgrading, this no longer works.

Original Code:

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    //Get Url
    RouteValueDictionary routes = null;
    if (routeValues != null)
        routes = new RouteValueDictionary(routeValues);
    else
        routes = new RouteValueDictionary();
    routes.Add("Action", actionName);
    if (!string.IsNullOrEmpty(controllerName))
        routes.Add("Controller", controllerName);
    else
        routes.Add("Controller", this.ControllerContext.RouteData.Values["Controller"].ToString());
    var url = RouteTable.Routes.GetVirtualPath(this.ControllerContext.RequestContext, routes).VirtualPath;

    //Rewrite path
    System.Web.HttpContext.Current.RewritePath(url, false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(System.Web.HttpContext.Current);
}

We receive errors on the httpHandler.ProcessRequest call. We used this technique in a number of places. After much googling it seemed that we should use Server.TransferRequest instead.

New Code

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    //Get Url
    RouteValueDictionary routes = null;
    if (routeValues != null)
        routes = new RouteValueDictionary(routeValues);
    else
        routes = new RouteValueDictionary();
    routes.Add("Action", actionName);
    if (!string.IsNullOrEmpty(controllerName))
        routes.Add("Controller", controllerName);
    else
        routes.Add("Controller", this.ControllerContext.RouteData.Values["Controller"].ToString());
    var url = RouteTable.Routes.GetVirtualPath(this.ControllerContext.RequestContext, routes).VirtualPath;

    //Rewrite path
    System.Web.HttpContext.Current.RewritePath(url, false);
    System.Web.HttpContext.Current.Server.TransferRequest(url, true);
}

When called from code like this:

IncludeAction("OptInBanner", "Person");
IncludeAction("NavMenu", "Person");
return Transfer(returnurl);

Our new code generates this error:

Type:
    System.InvalidOperationException
Message:
    TransferRequest cannot be invoked more than once.
Stack Trace:
    at System.Web.HttpServerUtility.TransferRequest(String path, Boolean preserveForm)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName, Object routeValues)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName)
    at MyProject.MyNamspace.MyController.MyAction(Boolean myChoice, String returnurl)
    at .lambda_method(Closure , ControllerBase , Object[] )

Since the message plainly says I cannot call TransferRequest more than once, but my code needs to execute two controller actions in addition to redirecting and performing a third action, I thought I'd revert to the old code. However, that generates this error:

Type:
    System.InvalidOperationException
Message:
    'HttpContext.SetSessionStateBehavior' can only be invoked before 'HttpApplication.AcquireRequestState' event is raised.
Stack Trace:
    at System.Web.Routing.UrlRoutingHandler.ProcessRequest(HttpContextBase httpContext)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName, Object routeValues)
    at MyProject.MyNamspace.MyBaseController.IncludeAction(String actionName, String controllerName)
    at MyProject.MyNamspace.MyController.MyAction(Boolean myChoice, String returnurl)
    at .lambda_method(Closure , ControllerBase , Object[] )

For this function, how can I retain the original behavior, without errors, that we had under .Net 4.0 and MVC 2.0 while using the newer framework and MVC?


回答1:


After a lot of research, I came up with this:

protected void IncludeAction(string actionName, string controllerName, object routeValues)
{
    string targetController = null;
    if (!string.IsNullOrWhiteSpace(controllerName))
    {
        targetController = controllerName;
    }
    else
    {
        targetController = this.ControllerContext.RouteData.Values["Controller"].ToString();
    }

    HtmlHelper htmlHelper = new HtmlHelper(
        new ViewContext(
            this.ControllerContext,
            new WebFormView(this.ControllerContext, actionName),
            this.ViewData,
            this.TempData,
            this.Response.Output
        ),
        new ViewPage()
    );

    htmlHelper.RenderAction(actionName, targetController, routeValues);
}

The HtmlHelper is constructed with a new ViewContext. The new ViewContext is constructed with much of the data from the current ControllerContext and the current Response object's TextWriter (this.Response.Output).

Calling RenderAction on the HtmlHelper will render my chosen Controller-Action results to the Response stream, allowing us to call multiple Controller Actions from other Controller Actions and letting our client side AJAX framework apply the results to the correct portions of the page.

For places where it is okay to throw out the pending Response stream, we can still use Server.TransferRequest. But this now allows us to append multiple Controller-Action results to the same response stream, from the server.



来源:https://stackoverflow.com/questions/26533724/execute-multiple-controller-actions-in-one-call-with-mvc-net-5

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