Permalink
Browse files

Add support for executing IHostingStartup in specified assemblies

- Assemblies that are specified in the "hostingStartupAssemblies" configuration (; delimited)
  setting can specify assemblies that use an assembly level attribute (HostingStartupAttribute)
  to specify a type that implements IHostingStartup. This allows hosting environments to
  extend the IWebHostBuilder with platform specific behavior before the application runs.
- If Startup fails and CaptureStartupErrors is true, we'll show the error in the usual
  Startup page.
- Added tests

#951
  • Loading branch information...
davidfowl committed Mar 11, 2017
1 parent 387e2d8 commit 1fb6b80cf5ee39e500c5c70df04848438ba87c98
@@ -0,0 +1,40 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Reflection;
namespace Microsoft.AspNetCore.Hosting
{
/// <summary>
/// Marker attribute indicating an implementation of <see cref="IHostingStartup"/> that will be loaded and executed when building an <see cref="IWebHost"/>.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)]
public sealed class HostingStartupAttribute : Attribute
{
/// <summary>
/// Constructs the <see cref="HostingStartupAttribute"/> with the specified type.
/// </summary>
/// <param name="hostingStartupType">A type that implements <see cref="IHostingStartup"/>.</param>
public HostingStartupAttribute(Type hostingStartupType)
{
if (hostingStartupType == null)
{
throw new ArgumentNullException(nameof(hostingStartupType));
}
if (!typeof(IHostingStartup).GetTypeInfo().IsAssignableFrom(hostingStartupType.GetTypeInfo()))
{
throw new ArgumentException($@"""{hostingStartupType}"" does not implement {typeof(IHostingStartup)}.", nameof(hostingStartupType));
}
HostingStartupType = hostingStartupType;
}
/// <summary>
/// The implementation of <see cref="IHostingStartup"/> that should be loaded when
/// starting an application.
/// </summary>
public Type HostingStartupType { get; }
}
}
@@ -0,0 +1,22 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNetCore.Hosting
{
/// <summary>
/// Represents platform specific configuration that will be applied to a <see cref="IWebHostBuilder"/> when building an <see cref="IWebHost"/>
/// </summary>
public interface IHostingStartup
{
/// <summary>
/// Configure the <see cref="IWebHostBuilder"/>.
/// </summary>
/// <remarks>
/// Configure is intended to be called before user code, allowing a user to overwrite any changes made.
/// </remarks>
/// <param name="builder"></param>
void Configure(IWebHostBuilder builder);
}
}
@@ -7,7 +7,8 @@ public static class WebHostDefaults
{
public static readonly string ApplicationKey = "applicationName";
public static readonly string StartupAssemblyKey = "startupAssembly";
public static readonly string HostingStartupAssembliesKey = "hostingStartupAssemblies";
public static readonly string DetailedErrorsKey = "detailedErrors";
public static readonly string EnvironmentKey = "environment";
public static readonly string WebRootKey = "webroot";
@@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting.Builder;
@@ -33,6 +34,7 @@ public class WebHost : IWebHost
private readonly IServiceProvider _hostingServiceProvider;
private readonly WebHostOptions _options;
private readonly IConfiguration _config;
private readonly ExceptionDispatchInfo _startupError;
private IServiceProvider _applicationServices;
private RequestDelegate _application;
@@ -47,7 +49,8 @@ public class WebHost : IWebHost
IServiceCollection appServices,
IServiceProvider hostingServiceProvider,
WebHostOptions options,
IConfiguration config)
IConfiguration config,
ExceptionDispatchInfo startupError)
{
if (appServices == null)
{
@@ -65,6 +68,7 @@ public class WebHost : IWebHost
}
_config = config;
_startupError = startupError;
_options = options;
_applicationServiceCollection = appServices;
_hostingServiceProvider = hostingServiceProvider;
@@ -143,6 +147,8 @@ private RequestDelegate BuildApplication()
{
try
{
_startupError?.Throw();
EnsureApplicationServices();
EnsureServer();
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNetCore.Hosting.Internal
@@ -26,10 +27,13 @@ public WebHostOptions(IConfiguration configuration)
Environment = configuration[WebHostDefaults.EnvironmentKey];
WebRoot = configuration[WebHostDefaults.WebRootKey];
ContentRootPath = configuration[WebHostDefaults.ContentRootKey];
PlatformAssemblies = configuration[WebHostDefaults.HostingStartupAssembliesKey]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
}
public string ApplicationName { get; set; }
public IReadOnlyList<string> PlatformAssemblies { get; set; }
public bool DetailedErrors { get; set; }
public bool CaptureStartupErrors { get; set; }
@@ -158,7 +158,7 @@ public IWebHost Build()
Console.WriteLine("The environment variable 'ASPNETCORE_SERVER.URLS' is obsolete and has been replaced with 'ASPNETCORE_URLS'");
}
var hostingServices = BuildCommonServices();
var hostingServices = BuildCommonServices(out var startupError);
var applicationServices = hostingServices.Clone();
var hostingServiceProvider = hostingServices.BuildServiceProvider();
@@ -168,15 +168,18 @@ public IWebHost Build()
applicationServices,
hostingServiceProvider,
_options,
_config);
_config,
startupError);
host.Initialize();
return host;
}
private IServiceCollection BuildCommonServices()
private IServiceCollection BuildCommonServices(out ExceptionDispatchInfo startupError)
{
startupError = null;
_options = new WebHostOptions(_config);
var appEnvironment = PlatformServices.Default.Application;
@@ -200,6 +203,25 @@ private IServiceCollection BuildCommonServices()
services.AddSingleton(_loggerFactory);
}
try
{
// Execute the platform light-up assemblies
foreach (var assemblyName in _options.PlatformAssemblies)
{
var assembly = Assembly.Load(new AssemblyName(assemblyName));
foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
{
var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
hostingStartup.Configure(this);
}
}
}
catch (Exception ex) when (_options.CaptureStartupErrors)
{
startupError = ExceptionDispatchInfo.Capture(ex);
}
foreach (var configureLogging in _configureLoggingDelegates)
{
configureLogging(_loggerFactory);
Oops, something went wrong.

0 comments on commit 1fb6b80

Please sign in to comment.