繁体   English   中英

如何在 app.UseEndPoints 中将自定义路由操作作为委托实例添加到 ASP.NET Core 3.1 应用程序?

[英]How to add custom routed action as a delegate instance to ASP.NET Core 3.1 app in app.UseEndPoints?

我在将路由映射到其处理程序时尝试使用本地委托实例,即返回IActionResult的 function ,就像在Controller中定义的常规Action一样。

这是我尝试过的方法:

using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();

        services.AddLocalization(options => options.ResourcesPath = "Resources");

        services.AddMvcCore();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
        }

        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("zh-CN"),
            new CultureInfo("ja-JP"),
        };
        app.UseRequestLocalization(new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture("en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        });

        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        RouteHandler actionA = () => new ContentResult {Content = "Hello A!"};
        ContentRouteHandler actionB = () => new ContentResult {Content = "Hello B!"};
        app.UseEndpoints(endpoints =>
        {
            endpoints.Map("HelloA", context => WriteActionResult(context, actionA()));
            endpoints.Map("HelloB", context => WriteActionResult(context, actionB()));
        });
    }

    public static Task WriteActionResult<TResult>(HttpContext context, TResult result) where TResult : IActionResult
    {
        var executor = context.RequestServices.GetRequiredService<IActionResultExecutor<TResult>>();
        var routeData = context.GetRouteData() ?? new RouteData();
        var actionContext = new ActionContext(context, routeData, new ActionDescriptor());

        return executor.ExecuteAsync(actionContext, result);
    }

    public delegate IActionResult RouteHandler();
    public delegate ContentResult ContentRouteHandler();
}

核心部分是

RouteHandler actionA = () => new ContentResult {Content = "Hello A!"};
ContentRouteHandler actionB = () => new ContentResult {Content = "Hello B!"};
app.UseEndpoints(endpoints =>
{
    endpoints.Map("HelloA", context => WriteActionResult(context, actionA()));
    endpoints.Map("HelloB", context => WriteActionResult(context, actionB()));
});

然后在浏览器中,响应请求http://localhost:59716/HelloA

执行context.RequestServices.GetRequiredService<IActionResultExecutor<TResult>>(); WriteActionResult<TResult>()

InvalidOperationException:没有为“Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor`1[Microsoft.AspNetCore.Mvc.IActionResult]”类型注册的服务。

但是请求http://localhost:59716/HelloB的响应是正常的:

你好乙

我已经阅读了IActionResultExecutor如何在services.AddMvcCore()中注册来源。

但是我不明白为什么它无法处理delegate IActionResult RouteHandler()类型的委托实例,而delegate ContentResult ContentRouteHandler()却成功了。

我的问题是如何解决这个问题?

使用反射

    public static Task WriteActionResult<TResult>(HttpContext context, TResult result)
    where TResult : class, IActionResult
{
    var resultType = result.GetType();
    var executorType = typeof(IActionResultExecutor<>).MakeGenericType(resultType);
    var executor = context.RequestServices.GetRequiredService(executorType);
    var routeData = context.GetRouteData() ?? new RouteData();
    var actionContext = new ActionContext(context, routeData, new ActionDescriptor());
    var method = executorType.GetMethod(nameof(IActionResultExecutor<IActionResult>.ExecuteAsync)) ?? throw new MissingMethodException($"Missing 'ExecuteAsync' method");
    return (Task)method.Invoke(executor, new[] {actionContext, Convert.ChangeType(result, resultType)});
}

对于第一条路线:

endpoints.Map("HelloA", context => WriteActionResult(context, actionA()));

您正在调用WriteActionResult<IActionResult>(context, actionA()) ,这会导致IActionResultExecutor<IActionResult>得到解决。

默认情况下没有注册IActionResultExecutor<IActionResult> 所以抛出异常。

但是,默认注册了IActionResultExecutor<ContentResult> 因此,当您调用第二条路线时:

endpoints.Map("HelloB", context => WriteActionResult<ContentResult>(context, actionB()));

它只是工作。

要修复第一条路线,请尝试以下操作:

endpoints.MapGet(
    "HelloA",
    context => WriteActionResult(context, (ContentResult)actionA())
);

或者您可以像这样修复RouteHandler

public delegate T RouteHandler<out T>() where T : IActionResult;

解决方案是使用dynamic

前:

RouteHandler actionA =
    () => new ContentResult {Content = "Hello A!"};

后:

Func<dynamic> actionA =
    () => new ContentResult {Content = "Hello A!"};

结果:
/HellA 的结果

在运行时, Func<dynamic>将被解析为实际类型Func<ContentResult> ,因此actionA()的返回类型将为ContentResult ,稍后可以由IActionResultExecutor<ContentResult>使用,该类型已经注册。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM