-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
API Proposal
namespace Microsoft.AspNetCore.Hosting.Infrastructure
{
+ public interface ISupportsStartup
+ {
+ IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure);
+ IWebHostBuilder UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType);
+ IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory);
+ }
+
+ public interface ISupportsConfigureWebHost
+ {
+ IHostBuilder ConfigureWebHost(Action<IWebHostBuilder> configure, Action<WebHostBuilderOptions> configureOptions);
+ }We would implement ISupportsStartup in ConfigureWebHostBuilder (i.e. WebApplicationBuilder.WebHost) and ISupportsConfigureWebHost in ConfigureHostBuilder (i.e. WebApplicationBuilder.Host) .
Justifcation
In order to make builder.WebHost.UseStartup and builder.WebHost.Configure throw instead of no-op in the following:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseStartup<Startup>();
var app = builder.Build();var builder = WebApplication.CreateBuilder(args);
builder.WebHost.Configure(app =>
{
app.Run(async context =>
{
await context.Response.WriteAsync("Hello world from configure!");
});
});
var app = builder.Build();This is because both UseStartup and Configure will try to pass through their calls to any IWebHostBuilder implementing ISupportsStartup giving us the opportunity to throw.
Currently only GenericWebHostBuilder implements ISupportsStartup and it lives in the same assembly so it can be internal. ConfigureWebHostBuilder is in another assembly that depends on Hosting, so it needs the interface to be public to implement it.
Alternative considered
-
namespace Microsoft.AspNetCore.Hosting { + public interface IRejectStartup + { + void ThrowException(); + }With this design, IRejectStartup is not just a marker interface, but it also throws an Exception via ThrowException() which would be called by the unsupported extension methods allowing any implementers of the interface to provide more detailed error messages. I'm not sure this is necessary, and if we wanted to support throwing different exceptions from Configure and UseStartup, the interface becomes more complicated.
-
namespace Microsoft.AspNetCore.Hosting { + public interface ISupportsStartup + { + IWebHostBuilder Configure(Action<WebHostBuilderContext, IApplicationBuilder> configure); + IWebHostBuilder UseStartup([DynamicallyAccessedMembers(StartupLinkerOptions.Accessibility)] Type startupType); + IWebHostBuilder UseStartup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]TStartup>(Func<WebHostBuilderContext, TStartup> startupFactory); + }This feels like it exposes more than we have to and doesn't allow us to throw from ConfigureWebHost().
-
If ConfigureWebHostBuilder continued not to implement ISupportsStartup, we could look for additions of IStartup services to the WebApplicationServiceCollection instead and throw if that ever happens. This feels a lot less straightforward than implementing ISupportsStartup however.
-
namespace Microsoft.AspNetCore.Hosting { + /// <summary> + /// A marker interface used to indicate the <see cref="IHostBuilder"/> or <see cref="IWebHostBuilder"/> + /// does not support any of the following extension methods: + /// * <see cref="WebHostBuilderExtensions.Configure(IWebHostBuilder, Action{AspNetCore.Builder.IApplicationBuilder})"/> + /// * <see cref="WebHostBuilderExtensions.UseStartup(IWebHostBuilder, Type)"/> + /// * <see cref="GenericHostWebHostBuilderExtensions.ConfigureWebHost(IHostBuilder, Action{IWebHostBuilder})"/> + /// </summary> + public interface IRejectStartup + { + }
This allows us to check inside these extension methods whether they should throw to indicate they're not supported by the given IHostBuilder or IWebHostBuilder (namely WebApplicationBuilder.Host/WebHost) rather than effectively doing nothing.