使用MVC.Net5在一个调用中执行多个控制器操作

本文关键字:调用 执行 操作 控制器 一个 Net5 MVC 使用 | 更新日期: 2023-09-27 18:27:32

我们最近将代码库从.Net 4.0升级到.Net 4.5.1,并从MVC 2.0升级到MVC 5.2.2。

我们在基本控制器类中有一个自定义方法,它允许我们在一个请求中更新视图的多个部分。升级后,此功能不再有效。

原始代码:

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);
}

我们在httpHandler.ProcessRequest调用中收到错误。我们在许多地方使用了这种技术。在谷歌上搜索了很多之后,我们似乎应该使用Server.TransferRequest

新代码

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);
}

当从这样的代码调用时:

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

我们的新代码产生了这个错误:

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[] )

由于消息明确表示我不能多次调用TransferRequest,但我的代码除了重定向和执行第三个操作外,还需要执行两个控制器操作,所以我想我应该恢复到旧代码。然而,这会产生这个错误:

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[] )

对于这个函数,在使用较新的框架和MVC时,我如何在没有错误的情况下保留我们在.Net 4.0和MVC 2.0下的原始行为?

使用MVC.Net5在一个调用中执行多个控制器操作

经过大量研究,我提出了这个:

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);
}

HtmlHelper是用一个新的ViewContext构造的。新的ViewContext是用当前ControllerContext和当前Response对象的TextWriter(this.Response.Output)中的大部分数据构建的。

在HtmlHelper上调用RenderAction将把我选择的控制器操作结果呈现到响应流,允许我们从其他控制器操作中调用多个控制器操作,并让我们的客户端AJAX框架将结果应用到页面的正确部分。

对于可以抛出挂起的响应流的地方,我们仍然可以使用Server.TransferRequest。但这现在允许我们从服务器将多个控制器操作结果附加到同一响应流中。