以编程方式调用 ASP.NET 核心请求管道
Programmatically invoking the ASP.NET Core request pipeline
问题
如果我有一个 HTTP 动词、路由、headers 和 body 负载,是否有办法从我自己的应用程序中以编程方式调用 ASP.NET 核心请求管道?
背景
use-cases ASP.NET 核心应用程序的 WebAPI 无法访问,因为该应用程序 运行 在防火墙后面或无法访问。
为了为这种情况提供解决方案,我们希望我们的应用程序为 "work-items" 轮询一些其他服务,然后在我们的应用程序中转换为 API 调用。
我考虑过的方法
- 我可能只要求 DI 给我一个控制器的实例,然后调用它的方法。这种方法的问题:
- 不强制执行授权属性。但是在我们的 use-case 中验证不记名令牌很重要。所以这里的问题是:如何以编程方式调用授权中间件?
- 我必须自己将传入的 work-items 路由到正确的 controller/method。
- 使用
Microsoft.AspNetCore.TestHost
包我可以创建一个 TestClient
允许我向自己发出请求(参见 here)。但这里有几个不确定因素:
- 此
TestHost
的预期 use-case 用于集成测试。在生产环境中使用它安全吗?
- 是否可以在常规托管的同时拥有这样的
TestServer
运行?
- thread-safety呢?我可以从单个
TestServer
实例创建多个 TestClients
并从不同线程使用它们吗?
所以我确信必须有一种更清晰、更直接的方法来从我自己的应用程序中以编程方式调用请求管道...
是的,这实际上相当容易。您可以在 Startup class Configure 方法的末尾获得对请求管道的引用。保存在静态字段/单例服务/等
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... usual configuration code
AClass.PipelineStaticField = app.Build();
}
然后,在您希望注入请求的方法中,您必须构建一个 HttpContext 以传递到管道中。
var ctx = new DefaultHttpContext();
// setup a DI scope for scoped services in your controllers etc.
using var scope = _provider.CreateScope();
ctx.RequestServices = scope.ServiceProvider;
// prepare the request as needed
ctx.Request.Body = new MemoryStream(...);
ctx.Request.ContentType = "application/json";
ctx.Request.ContentLength = 1234;
ctx.Request.Method = "POST";
ctx.Request.Path = PathString.FromUriComponent("/mycontroller/action");
// you only need this if you are hosting in IIS (.UseIISIntegration())
ctx.Request.Headers["MS-ASPNETCORE-TOKEN"] = Environment.GetEnvironmentVariable("ASPNETCORE_TOKEN");
// setup a place to hold the response body
ctx.Response.Body = new MemoryStream();
// execute the request
await AClass.PipelineStaticField(ctx);
// interpret the result as needed, e.g. parse the body
ctx.Response.Body.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(ctx.Response.Body);
string body = await reader.ReadToEndAsync();
这样您的请求将遍历整个管道,包括所有中间件,例如身份验证和授权。
问题
如果我有一个 HTTP 动词、路由、headers 和 body 负载,是否有办法从我自己的应用程序中以编程方式调用 ASP.NET 核心请求管道?
背景
use-cases ASP.NET 核心应用程序的 WebAPI 无法访问,因为该应用程序 运行 在防火墙后面或无法访问。
为了为这种情况提供解决方案,我们希望我们的应用程序为 "work-items" 轮询一些其他服务,然后在我们的应用程序中转换为 API 调用。
我考虑过的方法
- 我可能只要求 DI 给我一个控制器的实例,然后调用它的方法。这种方法的问题:
- 不强制执行授权属性。但是在我们的 use-case 中验证不记名令牌很重要。所以这里的问题是:如何以编程方式调用授权中间件?
- 我必须自己将传入的 work-items 路由到正确的 controller/method。
- 使用
Microsoft.AspNetCore.TestHost
包我可以创建一个TestClient
允许我向自己发出请求(参见 here)。但这里有几个不确定因素:- 此
TestHost
的预期 use-case 用于集成测试。在生产环境中使用它安全吗? - 是否可以在常规托管的同时拥有这样的
TestServer
运行? - thread-safety呢?我可以从单个
TestServer
实例创建多个TestClients
并从不同线程使用它们吗?
- 此
所以我确信必须有一种更清晰、更直接的方法来从我自己的应用程序中以编程方式调用请求管道...
是的,这实际上相当容易。您可以在 Startup class Configure 方法的末尾获得对请求管道的引用。保存在静态字段/单例服务/等
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... usual configuration code
AClass.PipelineStaticField = app.Build();
}
然后,在您希望注入请求的方法中,您必须构建一个 HttpContext 以传递到管道中。
var ctx = new DefaultHttpContext();
// setup a DI scope for scoped services in your controllers etc.
using var scope = _provider.CreateScope();
ctx.RequestServices = scope.ServiceProvider;
// prepare the request as needed
ctx.Request.Body = new MemoryStream(...);
ctx.Request.ContentType = "application/json";
ctx.Request.ContentLength = 1234;
ctx.Request.Method = "POST";
ctx.Request.Path = PathString.FromUriComponent("/mycontroller/action");
// you only need this if you are hosting in IIS (.UseIISIntegration())
ctx.Request.Headers["MS-ASPNETCORE-TOKEN"] = Environment.GetEnvironmentVariable("ASPNETCORE_TOKEN");
// setup a place to hold the response body
ctx.Response.Body = new MemoryStream();
// execute the request
await AClass.PipelineStaticField(ctx);
// interpret the result as needed, e.g. parse the body
ctx.Response.Body.Seek(0, SeekOrigin.Begin);
using var reader = new StreamReader(ctx.Response.Body);
string body = await reader.ReadToEndAsync();
这样您的请求将遍历整个管道,包括所有中间件,例如身份验证和授权。