[英]How to add custom routed action as a delegate instance to ASP.NET Core 3.1 app in app.UseEndPoints?
I'm trying to use an local delegate instance when mapping route against its handler, namely a function that returns IActionResult
, like a regular Action
defined in a Controller
.我在将路由映射到其处理程序时尝试使用本地委托实例,即返回
IActionResult
的 function ,就像在Controller
中定义的常规Action
一样。
Here is how I have tried:这是我尝试过的方法:
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();
}
The core part is核心部分是
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()));
});
Then in the browser, the response to request http://localhost:59716/HelloA
:然后在浏览器中,响应请求
http://localhost:59716/HelloA
:
An error occured when executing context.RequestServices.GetRequiredService<IActionResultExecutor<TResult>>();
执行
context.RequestServices.GetRequiredService<IActionResultExecutor<TResult>>();
in WriteActionResult<TResult>()
:在
WriteActionResult<TResult>()
:
InvalidOperationException: No service for type 'Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor`1[Microsoft.AspNetCore.Mvc.IActionResult]' has been registered.
InvalidOperationException:没有为“Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor`1[Microsoft.AspNetCore.Mvc.IActionResult]”类型注册的服务。
But the response to request http://localhost:59716/HelloB
is normal:但是请求
http://localhost:59716/HelloB
的响应是正常的:
I have read the source of how IActionResultExecutor
is registered here in services.AddMvcCore()
.我已经阅读了
IActionResultExecutor
如何在services.AddMvcCore()
中注册的来源。
But I failed to understand why it failed to handle a delegate instance of type delegate IActionResult RouteHandler()
while it succeeded for delegate ContentResult ContentRouteHandler()
.但是我不明白为什么它无法处理
delegate IActionResult RouteHandler()
类型的委托实例,而delegate ContentResult ContentRouteHandler()
却成功了。
My question is how to solve this problem?我的问题是如何解决这个问题?
Use reflection使用反射
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)});
}
For the first route:对于第一条路线:
endpoints.Map("HelloA", context => WriteActionResult(context, actionA()));
You are calling WriteActionResult<IActionResult>(context, actionA())
, in which causes IActionResultExecutor<IActionResult>
to be resolved.您正在调用
WriteActionResult<IActionResult>(context, actionA())
,这会导致IActionResultExecutor<IActionResult>
得到解决。
There is no IActionResultExecutor<IActionResult>
by default registered.默认情况下没有注册
IActionResultExecutor<IActionResult>
。 So the exception is thrown.所以抛出异常。
However, there is IActionResultExecutor<ContentResult>
by default registerd.但是,默认注册了
IActionResultExecutor<ContentResult>
。 So when you call the second route:因此,当您调用第二条路线时:
endpoints.Map("HelloB", context => WriteActionResult<ContentResult>(context, actionB()));
it just works.它只是工作。
To fix first route, try this:要修复第一条路线,请尝试以下操作:
endpoints.MapGet(
"HelloA",
context => WriteActionResult(context, (ContentResult)actionA())
);
Or you could fix RouteHandler
like:或者您可以像这样修复
RouteHandler
:
public delegate T RouteHandler<out T>() where T : IActionResult;
The solution is to use dynamic
:解决方案是使用
dynamic
:
Before:前:
RouteHandler actionA =
() => new ContentResult {Content = "Hello A!"};
After:后:
Func<dynamic> actionA =
() => new ContentResult {Content = "Hello A!"};
Result:结果:
At runtime, Func<dynamic>
will be resolved to the actual type Func<ContentResult>
, so the return type of actionA()
will be ContentResult
and can be consumed later by IActionResultExecutor<ContentResult>
, which has already been registered.在运行时,
Func<dynamic>
将被解析为实际类型Func<ContentResult>
,因此actionA()
的返回类型将为ContentResult
,稍后可以由IActionResultExecutor<ContentResult>
使用,该类型已经注册。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.