簡體   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