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?
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);
};
});
}
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
});
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With