【问题标题】:What are the difference using app.Run and app.UseEndpoints in ASP.NET Core?在 ASP.NET Core 中使用 app.Run 和 app.UseEndpoints 有什么区别?
【发布时间】: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


    【解决方案1】:

    对于app.Run,它将终端中间件委托添加到应用程序的请求管道。

    对于app.Use,它将中间件委托添加到应用程序的请求管道。

    app.Runapp.UseEndpoints的区别,就是app.Runapp.Use的区别。 app.Run 将结束请求,app.Use 将请求传递给下一个中间件。

    对于app.UseEndpoints,它是app.UseEndpointMiddleware

    一些关键代码如:

        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);
                };
            });
        }
    

    【讨论】:

      【解决方案2】:

      区别是基本的,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 文档。

      我建议你阅读这些:

      【讨论】:

        【解决方案3】:

        运行: 终止链。在此之后不会运行其他中间件方法。应放置在任何管道的末端。

        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
        });
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-17
          • 1970-01-01
          • 2021-12-30
          • 1970-01-01
          • 2021-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多