【发布时间】:2020-02-14 09:29:30
【问题描述】:
我正在使用 ASP.NET Core 并试图找出 app.Run() 和 app.UseEndpoints() 之间的区别。它们有一些优点/缺点吗?我尝试在 3.0 中使用 app.Run() 但我不确定是否有必要?有人可以建议吗?
【问题讨论】:
标签: c# .net asp.net-core .net-core asp.net-core-2.0
我正在使用 ASP.NET Core 并试图找出 app.Run() 和 app.UseEndpoints() 之间的区别。它们有一些优点/缺点吗?我尝试在 3.0 中使用 app.Run() 但我不确定是否有必要?有人可以建议吗?
【问题讨论】:
标签: c# .net asp.net-core .net-core asp.net-core-2.0
对于app.Run,它将终端中间件委托添加到应用程序的请求管道。
对于app.Use,它将中间件委托添加到应用程序的请求管道。
app.Run和app.UseEndpoints的区别,就是app.Run和app.Use的区别。 app.Run 将结束请求,app.Use 将请求传递给下一个中间件。
对于app.UseEndpoints,它是app.Use 和EndpointMiddleware。
一些关键代码如:
public static IApplicationBuilder UseEndpoints(this IApplicationBuilder builder, Action<IEndpointRouteBuilder> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
VerifyRoutingServicesAreRegistered(builder);
VerifyEndpointRoutingMiddlewareIsRegistered(builder, out var endpointRouteBuilder);
configure(endpointRouteBuilder);
// Yes, this mutates an IOptions. We're registering data sources in a global collection which
// can be used for discovery of endpoints or URL generation.
//
// Each middleware gets its own collection of data sources, and all of those data sources also
// get added to a global collection.
var routeOptions = builder.ApplicationServices.GetRequiredService<IOptions<RouteOptions>>();
foreach (var dataSource in endpointRouteBuilder.DataSources)
{
routeOptions.Value.EndpointDataSources.Add(dataSource);
}
return builder.UseMiddleware<EndpointMiddleware>();
}
UseMidleware 类似于
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
{
if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
{
// IMiddleware doesn't support passing args directly since it's
// activated from the container
if (args.Length > 0)
{
throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
}
return UseMiddlewareInterface(app, middleware);
}
var applicationServices = app.ApplicationServices;
return app.Use(next =>
{
var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
var invokeMethods = methods.Where(m =>
string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)
|| string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)
).ToArray();
if (invokeMethods.Length > 1)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName));
}
if (invokeMethods.Length == 0)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware));
}
var methodInfo = invokeMethods[0];
if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task)));
}
var parameters = methodInfo.GetParameters();
if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext))
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext)));
}
var ctorArgs = new object[args.Length + 1];
ctorArgs[0] = next;
Array.Copy(args, 0, ctorArgs, 1, args.Length);
var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
if (parameters.Length == 1)
{
return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);
}
var factory = Compile<object>(methodInfo, parameters);
return context =>
{
var serviceProvider = context.RequestServices ?? applicationServices;
if (serviceProvider == null)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}
return factory(instance, context, serviceProvider);
};
});
}
【讨论】:
区别是基本的,Edward 做了很好的解释,但我相信更简单的解释会更好。 App.Use 用于向 OWIN 管道添加中间件,App.Run 也用于相同目的。不同之处在于,一旦使用 App.Run 添加的中间件完成其执行,管道将终止并且响应将返回给调用者。这是唯一的区别。举个例子吧。
app.Use((context, nextMidWare) => { context.Response.Body.Write("Written by app.Use"); nextMidWare(context);});
app.Run((context) => context.Response.Body.Write("Written by app.Run"));
app.Use((context, nextMidWare) => context.Response.Body.Write("Also written by app.Use"));
为了更好地传达我的解释,我稍微简化了方法签名。鉴于这些是唯一注册的中间件,当您从浏览器请求网站时,结果将如下所示。
Written by app.Use
Written by app.Run
您可以观察到最后一条消息“也由 app.Use 编写”尚未写入响应。原因当然是我们在使用 App.Run 注册了另一个中间件之后,又注册了关联的中间件。如果我们改用 App.Use,那么我们也会观察到最后一条消息。
最后,App.UseEndpoints 类似于对 App.Use 方法的预配置调用,它为您提供某些功能,我建议您阅读一些有关此问题的 Microsoft 文档。
我建议你阅读这些:
【讨论】:
运行: 终止链。在此之后不会运行其他中间件方法。应放置在任何管道的末端。
app.Run(async context =>
{
await context.Response.WriteAsync("Hello from " + _environment);
});
使用: 在下一个委托之前和之后执行操作。
app.Use(async (context, next) =>
{
//action before next delegate
await next.Invoke(); //call next middleware
//action after called middleware
});
【讨论】: