Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the difference using app.Run and app.UseEndpoints in ASP.NET Core?

I'm using ASP.NET Core and am trying to work out the difference between app.Run() and app.UseEndpoints(). Are there some advantages / disadvantages of them? I tried to use app.Run() in 3.0 but I'm not sure if it is necessary or not? Can someone advise?

like image 659
davinceleecode Avatar asked Oct 17 '19 12:10

davinceleecode


Video Answer


3 Answers

For app.Run, it adds a terminal middleware delegate to the application's request pipeline.

For app.Use, it adds a middleware delegate to the application's request pipeline.

For the difference between app.Run and app.UseEndpoints, it is the difference between app.Run and app.Use. app.Run will end the request, and app.Use will pass the request to next middleware.

For app.UseEndpoints, it is app.Use with EndpointMiddleware.

Some key code like:

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

The UseMidleware is something like

    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);
            };
        });
    }
like image 102
Edward Avatar answered Nov 08 '22 22:11

Edward


Run: Terminates chain. No other middleware method will run after this. Should be placed at the end of any pipeline.

app.Run(async context =>
{
  await context.Response.WriteAsync("Hello from " + _environment);
});

Use: Performs action before and after next delegate.

app.Use(async (context, next) =>
{
    //action before next delegate
    await next.Invoke(); //call next middleware
    //action after called middleware
});
like image 23
Arshman Saleem Avatar answered Nov 08 '22 20:11

Arshman Saleem


The difference is basic, Edward has done great job explaining it but I believe a more simple explanation would be good. App.Use is used for adding middle ware to the OWIN pipeline, App.Run is also used for the same purpose. The difference is once a middle ware added with App.Run has completed its execution the pipeline will terminate and the response will be returned to the caller. That is the only difference. Let me give an example.

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

I've simplified the method signatures just a little in order to better convey my explanation. Given that these are the only middle wares registered, when you request the website from your browser the result will read as below.

Written by app.Use
Written by app.Run

As you can observe the last message "Also written by app.Use" has not been written to the response. The reason is, of course, that we have registered the associated middle ware after registering another middle ware using App.Run. Had we used App.Use instead then we would have observed the last message too.

And finally, the App.UseEndpoints is analogical to a pre-configured call to the App.Use method which gives you certain functionality, I suggest you to read some Microsoft docs on that matter.

I suggest you reading these:

  • http://www.c-sharpcorner.com/article/understanding-asp-net-part-one-owin-and-katana/
  • https://www.c-sharpcorner.com/article/understanding-asp-net-part-two-building-an-owin-pipeline/
like image 36
Efe Zaladin Avatar answered Nov 08 '22 22:11

Efe Zaladin