Asp.Net Core中的@ Html.Action

时间:2014-11-13 19:17:35

标签: asp.net-mvc asp.net-core

Asp.net Core中的@Html.Action在哪里? 我可以看到@Html.ActionLink但不能像以前那样直接调用Action。

是否被ViewComponents取代?

9 个答案:

答案 0 :(得分:27)

是的,ViewComponents将是实现此目的的新方式,但它们@Html.Action之前正在做的完全相同但是...例如,在MVC5和之前,调用'子动作'也会执行任何过滤器(例如,如果控制器上有装饰过滤器)给它们作为常规动作的外观......但是对于ViewComponents并不是这样,它们是在实际的上下文中执行的请求...

有关视图组件的更多信息: https://docs.microsoft.com/en-us/aspnet/core/mvc/views/view-components

答案 1 :(得分:12)

ViewComponents很棒,但对于Ajax来说并不是那么好。

如果你真的错过了@ Html.RenderAction方法,那么这里是我为AspNetCore整理的快速实现。

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing;

namespace Microsoft.AspNetCore.Mvc.Rendering    {

    public static class HtmlHelperViewExtensions
    {

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
        {
            var controller =  (string)helper.ViewContext.RouteData.Values["controller"];

            return RenderAction(helper, action, controller, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
        {
            var area = (string)helper.ViewContext.RouteData.Values["area"];

            return RenderAction(helper, action, controller, area, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (controller == null)
                throw new ArgumentNullException("controller");

            if (area == null)
                throw new ArgumentNullException("area");

            var task = RenderActionAsync(helper, action, controller, area, parameters);

            return task.Result;
        }

        private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            // fetching required services for invocation
            var currentHttpContext = helper.ViewContext?.HttpContext;
            var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
            var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
            var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext);

            // creating new action invocation context
            var routeData = new RouteData();
            var routeParams = new RouteValueDictionary(parameters ?? new { });
            var routeValues = new RouteValueDictionary(new { area = area, controller = controller, action = action });
            var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);

            newHttpContext.Response.Body = new MemoryStream();

            foreach (var router in helper.ViewContext.RouteData.Routers)
                routeData.PushState(router, null, null);

            routeData.PushState(null, routeValues, null);
            routeData.PushState(null, routeParams, null);

            var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First();
            var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);

            // invoke action and retreive the response body
            var invoker = actionInvokerFactory.CreateInvoker(actionContext);
            string content = null;

            await invoker.InvokeAsync().ContinueWith(task => {
                if (task.IsFaulted)
                {
                    content = task.Exception.Message;
                }
                else if (task.IsCompleted)
                {
                    newHttpContext.Response.Body.Position = 0;
                    using (var reader = new StreamReader(newHttpContext.Response.Body))
                        content = reader.ReadToEnd();
                }
            });

            return new HtmlString(content);
        }

        private static TService GetServiceOrFail<TService>(HttpContext httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            var service = httpContext.RequestServices.GetService(typeof(TService));

            if (service == null)
                throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");

            return (TService)service;
        }
    }
}

您可以使用以下方法之一从视图中调用:

@Html.RenderAction("action", "controller", "area", new { id = 1})
@Html.RenderAction("action", "controller", new { id = 1})
@Html.RenderAction("action", new { id = 1})

注意:

如果未提供,控制器名称和可选的区域名称将默认为ActionContext中的相应值。

答案 2 :(得分:11)

对于asp.net核心2

using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Mvc.Rendering
{
    public static class HtmlHelperViewExtensions
    {
        public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null)
        {
            var controller = (string)helper.ViewContext.RouteData.Values["controller"];

            return Action(helper, action, controller, parameters);
        }

        public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null)
        {
            var area = (string)helper.ViewContext.RouteData.Values["area"];

            return Action(helper, action, controller, area, parameters);
        }

        public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (controller == null)
                throw new ArgumentNullException("controller");


            var task = RenderActionAsync(helper, action, controller, area, parameters);

            return task.Result;
        }

        private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            // fetching required services for invocation
            var serviceProvider = helper.ViewContext.HttpContext.RequestServices;
            var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IActionContextAccessor>();
            var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService<IHttpContextAccessor>();
            var actionSelector = serviceProvider.GetRequiredService<IActionSelector>();

            // creating new action invocation context
            var routeData = new RouteData();
            foreach (var router in helper.ViewContext.RouteData.Routers)
            {
                routeData.PushState(router, null, null);
            }
            routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null);
            routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null);

            //get the actiondescriptor
            RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData };
            var candidates = actionSelector.SelectCandidates(routeContext);
            var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);

            var originalActionContext = actionContextAccessor.ActionContext;
            var originalhttpContext = httpContextAccessor.HttpContext;
            try
            {
                var newHttpContext = serviceProvider.GetRequiredService<IHttpContextFactory>().Create(helper.ViewContext.HttpContext.Features);
                if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))
                {
                    newHttpContext.Items.Remove(typeof(IUrlHelper));
                }
                newHttpContext.Response.Body = new MemoryStream();
                var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
                actionContextAccessor.ActionContext = actionContext;
                var invoker = serviceProvider.GetRequiredService<IActionInvokerFactory>().CreateInvoker(actionContext);
                await invoker.InvokeAsync();
                newHttpContext.Response.Body.Position = 0;
                using (var reader = new StreamReader(newHttpContext.Response.Body))
                {
                    return new HtmlString(reader.ReadToEnd());
                }
            }
            catch (Exception ex)
            {
                return new HtmlString(ex.Message);
            }
            finally
            {
                actionContextAccessor.ActionContext = originalActionContext;
                httpContextAccessor.HttpContext = originalhttpContext;
                if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper)))
                {
                    helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper));
                }
            }
        }
    }
}

它基于白羊座的回应。我纠正了没有编译2.0的内容,我添加了几个调整。当前的httpcontext和当前的actioncontext有2个美化的静态值。 httpcontext的一个在IHttpContextFactory.Create中设置,我在代码中为actioncontext设置了一个。请注意,根据您使用的功能IActionContextAccessorIHttpContextAccessor,默认情况下可能无法注册,因此您可能需要在启动时添加它们:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>(); services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

HttpContext只是HttpContext.Features的一个包装器,所以如果你在一个中改变某些东西,它也会在另一个中改变...我在try / catch的finally中重置了我所知道的东西。

我从Items缓存中删除了IUrlHelper,因为即使构建urlHelper的actionContext不同(IUrlHelperFactory.GetUrlHelper),也会重复使用此值。

Asp.net核心2.0假设你不会这样做,很有可能还有其他缓存的东西,所以我建议你在使用它时要小心,如果你不需要,就不要这样做。 / p>

答案 3 :(得分:2)

对于Net Core 2.0

using Microsoft.AspNetCore.Mvc.Infrastructure;

替换

// var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext); 
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext); 

// var actionDescriptor = actionSelector.DecisionTree.Select(routeValues).First(); 
var actionDescriptor = actionSelector.ActionDescriptors.Items.Where(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action).First();

答案 4 :(得分:1)

由于IActionSelectorDecisionTreeProvider已从较新版本中删除,因此Aries for Helper Extension的解决方法对Net Core 2.0不再适用。请参阅以下链接。

log4j2 Automation Configuration

答案 5 :(得分:1)

对于Yepeekai提供的asp.net core 2解决方案,请在您的Startup.cs中添加以下内容:

services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();

默认情况下,它不再在2.0中注册。

答案 6 :(得分:0)

我使用此页面上的人员代码来获得正确的结果。

https://stackoverflow.com/a/39951006/6778726

https://stackoverflow.com/a/46859170/6778726

例如,在旧类中,执行以下代码时,显示错误

@Html.RenderAction("About", "Home")

以下代码已已修复:

using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;

namespace Microsoft.AspNetCore.Mvc.Rendering
{

    public static class HtmlHelperViewExtensions
    {
        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
        {
            var controller = (string)helper.ViewContext.RouteData.Values["controller"];
            return RenderAction(helper, action, controller, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
        {
            var area = (string)helper.ViewContext.RouteData.Values["area"];
            return RenderAction(helper, action, controller, area, parameters);
        }

        public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            if (action == null)
                throw new ArgumentNullException(nameof(controller));
            if (controller == null)
                throw new ArgumentNullException(nameof(action));

            var task = RenderActionAsync(helper, action, controller, area, parameters);
            return task.Result;
        }

        private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
        {
            // fetching required services for invocation
            var currentHttpContext = helper.ViewContext.HttpContext;
            var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
            var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
            var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext);

            // creating new action invocation context
            var routeData = new RouteData();
            var routeParams = new RouteValueDictionary(parameters ?? new { });
            var routeValues = new RouteValueDictionary(new { area, controller, action });
            var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);

            newHttpContext.Response.Body = new MemoryStream();

            foreach (var router in helper.ViewContext.RouteData.Routers)
                routeData.PushState(router, null, null);

            routeData.PushState(null, routeValues, null);
            routeData.PushState(null, routeParams, null);

            var actionDescriptor = actionSelector.ActionDescriptors.Items.First(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action);
            var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);

            // invoke action and retreive the response body
            var invoker = actionInvokerFactory.CreateInvoker(actionContext);
            string content = null;

            await invoker.InvokeAsync().ContinueWith(task =>
            {
                if (task.IsFaulted)
                {
                    content = task.Exception.Message;
                }
                else if (task.IsCompleted)
                {
                    newHttpContext.Response.Body.Position = 0;
                    using (var reader = new StreamReader(newHttpContext.Response.Body))
                        content = reader.ReadToEnd();
                }
            });

            return new HtmlString(content);
        }

        private static TService GetServiceOrFail<TService>(HttpContext httpContext)
        {
            if (httpContext == null)
                throw new ArgumentNullException(nameof(httpContext));

            var service = httpContext.RequestServices.GetService(typeof(TService));

            if (service == null)
                throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");

            return (TService)service;
        }
    }
}

以下示例已成功测试:

@Html.RenderAction("About")
@Html.RenderAction("About", "Home")
@Html.RenderAction("About", new { data1 = "test1", data2 = "test2" })
@Html.RenderAction("About", "Home", new { data1 = "test1", data2 = "test2" })

答案 7 :(得分:0)

  

M.R.T2017说:...以下示例已成功完成   经过测试:...

首先感谢您的分享。

但是此重载方法可能导致“ HTTP ERROR 500”:

@Html.RenderAction("About")

因为“控制器”可能是小写的控制器名称,例如。 “家”,“网格”等:

helper.ViewContext.RouteData.Values["controller"]

您需要大写控制器名称,例如。 “ grid”->“ Grid”,因为控制器类名称在Assembly中区分大小写,所以动作名称相同。

* Visual Studio 2019 / NET Core 2.2。

答案 8 :(得分:0)

@ Html.Action被ViewComponents取代。我不喜欢ViewComponents的原因有很多。

但是我对@ Html.Action使用替代模式

首先,我在控制器上创建操作,该操作返回具有要显示在页面即页面中的内容的部分视图。

    [HttpGet]
    public async Task<IActionResult> GetFoo()
    {
        return PartialView("_Foo", new Foo());
    }

然后我将div放在应该加载foo视图的页面上,并在该页面的底部包括IIFE。 即下面的代码将加载GetFoo视图,然后将该HTML插入ID为foo-view的div中。

<div class="page">
    <div id="foo-view" data-url="@Url.Action(action: "GetFoo", controller: "Home")"></div>
</div>

<script>
    $(document).ready(function () {
        (function () {
            var url = $("#foo-view").data("url");
            $("#foo-view").load(url);
        })();
    });
</script>

从服务器获取视图时,您可能还想显示微调框。