Is there a way to programmatically invoke the ASP.NET Core request pipeline from within my own application, given I have a HTTP verb, the route, headers and body payload?
There are use-cases where the WebAPI of our ASP.NET Core application is not accessible because the application is running behind a firewall or is otherwise not reachable.
To provide a solution for this scenario we want our application to poll some other service for "work-items" which then translate into API calls in our application.
Microsoft.AspNetCore.TestHost
package I could create a TestClient
which allows me to make requests to myself (see here). But there are a couple of uncertainties here:
TestHost
is for integration testing. Is it safe to use this in a production environment?TestServer
running alongside the regular hosting?TestClients
from a single TestServer
instance and use them from different threads?So I'm sure there must be a cleaner and more direct way to programmatically invoke the request pipeline from within my own application...
Yes, it is actually fairly easy. You can get a reference to the request pipeline at the end of your Startup class Configure method. Save it in a static field / singleton service / etc.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... usual configuration code
AClass.PipelineStaticField = app.Build();
}
Then, in the method you wish to inject the request, you have to build a HttpContext to pass into the pipeline.
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();
That way your request will traverse the whole pipeline including all the middleware such as authentication and authorization.