从 ASP .NET Core 中的任意 URL 手动实例化控制器实例



我想仅使用路径和正文来执行 ASP.NET 核心(2.1)请求。我正在寻找这样的东西:

string path = "/controller/method";
string body = "{}";
ExecuteRoute(path, body); // this calls Controller.Method(body)

目前,我正在通过实际在服务器端发出HTTP请求来实现这一点。我希望通过执行没有 HTTP 请求的代码来以尽可能少的开销实现这一目标。我担心不必要的HTTP请求会导致高负载问题。

你想实现什么?我真的不明白你为什么要这样做,我为我能想到的用例写了两个解决方案。

  1. 如果您希望整个请求管道正常工作,那么最好的apparoach就是您目前正在做的事情,即向localhost发出http请求。但你是对的,它比简单的方法调用需要更多的资源。

  2. 如果您只想在控制器中调用方法(控制器操作),那么您应该重新组织逻辑并创建一个可以实例化的类,一个简单的方法调用就可以解决您的问题。

我在这个问题上挣扎了很多,终于找到了一个解决方案,这里解释了,但它有一个问题我仍在尝试解决。

我正在调用/three,其中包含对/one 和/two 的批处理请求。所以我希望在/three 运行之前运行/one 和/two,并将/three 的响应返回给客户端。

发生的情况是,当代码到达/two 时,我将收到错误"无法修改响应,因为它已经发送。所以/first 请求修改了原始请求的响应(即/three),我不能再写响应了。

public class BatchController : Controller
{
    // inject this to controller via constructor
    IHttpContextFactory contextFactory;
    public async override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        IRouteCollection router = RouteData.Routers.OfType<IRouteCollection>().First();
        // run batched requets one by one
        foreach(...)
        {
            HttpContext context = contextFactory.Create(HttpContext.Features);
            // set request data
            context.Request.Path = path;
            context.Request.Body = body;
            context.Request.Headers = headers;
            var routeContext = new RouteContext(context);
            await router.RouteAsync(routeContext); // get route
            await routeContext.Handler.Invoke(context); // call controller
        }
        // continue
        await next();
    }
    [HttpGet("one")] public string One() { return "one"; }
    [HttpGet("two")] public string Two() { return "two"; }
    [HttpGet("three")] public string Three() { return "three"; }
}

我试图通过将控制器方法更改为异步任务(因此不需要返回)并使用 Response.WriteAsync 手动编写响应来解决这个问题,但这会在 AspNet 内部代码中的某个地方给出 nullreference 异常,我还没有找出原因。

[16:12:50 ERR] Connection id "0HLJVLA0U0328", Request id "0HLJVLA0U0328:00000001": An unhandled exception was thrown by the application.
System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.SaveTempDataFilter.<>c__DisplayClass3_0.<OnResourceExecuting>b__0(Object state)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FireOnStartingMayAwait(Stack`1 onStarting)
[16:12:50 ERR] An unhandled exception has occurred while executing the request.
System.ObjectDisposedException: The response has been aborted due to an unhandled application exception. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at Microsoft.AspNetCore.Mvc.ViewFeatures.Internal.SaveTempDataFilter.<>c__DisplayClass3_0.<OnResourceExecuting>b__0(Object state)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.FireOnStartingMayAwait(Stack`1 onStarting)
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ThrowResponseAbortedException()
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.InitializeResponseAsync(Int32 firstWriteByteCount)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.WriteAsync(ReadOnlyMemory`1 data, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpResponseStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.ResponseCompression.BodyWrapperStream.WriteAsync(Byte[] buffer, Int32 offset, Int32 count, CancellationToken cancellationToken)
   at ... await Response.WriteAsync(...)

最新更新