| File: WebHostBuilderExtensions.cs | Web Access |
| Project: src\aspnetcore\src\Hosting\TestHost\src\Microsoft.AspNetCore.TestHost.csproj (Microsoft.AspNetCore.TestHost) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Net.Http; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Shared; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; namespace Microsoft.AspNetCore.TestHost; /// <summary> /// Contains extensions for configuring the <see cref="IWebHostBuilder" /> instance. /// </summary> public static class WebHostBuilderExtensions { private static readonly string[] _defaultSolutionNames = ["*.sln", "*.slnx"]; /// <summary> /// Enables the <see cref="TestServer" /> service. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder UseTestServer(this IWebHostBuilder builder) { return builder.ConfigureServices(services => { services.AddSingleton<IHostLifetime, NoopHostLifetime>(); services.AddSingleton<IServer, TestServer>(); }); } /// <summary> /// Enables the <see cref="TestServer" /> service. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="configureOptions">Configures test server options</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder UseTestServer(this IWebHostBuilder builder, Action<TestServerOptions> configureOptions) { return builder.ConfigureServices(services => { services.Configure(configureOptions); services.AddSingleton<IHostLifetime, NoopHostLifetime>(); services.AddSingleton<IServer, TestServer>(); }); } /// <summary> /// Retrieves the TestServer from the host services. /// </summary> /// <param name="host"></param> /// <returns></returns> [Obsolete("IWebHost is obsolete. Use IHost instead. For more information, visit https://aka.ms/aspnet/deprecate/008.", DiagnosticId = "ASPDEPR008", UrlFormat = Obsoletions.AspNetCoreDeprecate008Url)] public static TestServer GetTestServer(this IWebHost host) { return (TestServer)host.Services.GetRequiredService<IServer>(); } /// <summary> /// Retrieves the test client from the TestServer in the host services. /// </summary> /// <param name="host"></param> /// <returns></returns> [Obsolete("IWebHost is obsolete. Use IHost instead. For more information, visit https://aka.ms/aspnet/deprecate/008.", DiagnosticId = "ASPDEPR008", UrlFormat = Obsoletions.AspNetCoreDeprecate008Url)] public static HttpClient GetTestClient(this IWebHost host) { return host.GetTestServer().CreateClient(); } /// <summary> /// Configures the <see cref="IWebHostBuilder" /> instance with the services provided in <paramref name="servicesConfiguration" />. /// </summary> /// <param name="webHostBuilder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="servicesConfiguration">An <see cref="Action"/> that registers services onto the <see cref="IServiceCollection"/>.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder ConfigureTestServices(this IWebHostBuilder webHostBuilder, Action<IServiceCollection> servicesConfiguration) { ArgumentNullException.ThrowIfNull(webHostBuilder); ArgumentNullException.ThrowIfNull(servicesConfiguration); if (webHostBuilder.GetType().Name.Equals("GenericWebHostBuilder", StringComparison.Ordinal)) { // Generic host doesn't need to do anything special here since there's only one container. webHostBuilder.ConfigureServices(servicesConfiguration); } else { #pragma warning disable CS0612 // Type or member is obsolete webHostBuilder.ConfigureServices( s => s.AddSingleton<IStartupConfigureServicesFilter>( new ConfigureTestServicesStartupConfigureServicesFilter(servicesConfiguration))); #pragma warning restore CS0612 // Type or member is obsolete } return webHostBuilder; } /// <summary> /// Configures the <see cref="IWebHostBuilder" /> instance with the services provided in <paramref name="servicesConfiguration" />. /// </summary> /// <param name="webHostBuilder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="servicesConfiguration">An <see cref="Action"/> that registers services onto the <typeparamref name="TContainer"/>.</param> /// <typeparam name="TContainer">A collection of service descriptors.</typeparam> /// <returns></returns> public static IWebHostBuilder ConfigureTestContainer<TContainer>(this IWebHostBuilder webHostBuilder, Action<TContainer> servicesConfiguration) { ArgumentNullException.ThrowIfNull(webHostBuilder); ArgumentNullException.ThrowIfNull(servicesConfiguration); #pragma warning disable CS0612 // Type or member is obsolete webHostBuilder.ConfigureServices( s => s.AddSingleton<IStartupConfigureContainerFilter<TContainer>>( new ConfigureTestServicesStartupConfigureContainerFilter<TContainer>(servicesConfiguration))); #pragma warning restore CS0612 // Type or member is obsolete return webHostBuilder; } /// <summary> /// Sets the content root of relative to the <paramref name="solutionRelativePath" />. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="solutionRelativePath">The directory of the solution file.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath) { return builder.UseSolutionRelativeContentRoot(solutionRelativePath, AppContext.BaseDirectory, _defaultSolutionNames); } /// <summary> /// Sets the content root of relative to the <paramref name="solutionRelativePath" />. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="solutionRelativePath">The directory of the solution file.</param> /// <param name="solutionName">The name of the solution file to make the content root relative to.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath, string solutionName) { return builder.UseSolutionRelativeContentRoot(solutionRelativePath, AppContext.BaseDirectory, [solutionName]); } /// <summary> /// Sets the content root of relative to the <paramref name="solutionRelativePath" />. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="solutionRelativePath">The directory of the solution file.</param> /// <param name="applicationBasePath">The root of the app's directory.</param> /// <param name="solutionName">The name of the solution file to make the content root relative to.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath, string applicationBasePath, string solutionName) { return builder.UseSolutionRelativeContentRoot(solutionRelativePath, applicationBasePath, [solutionName]); } /// <summary> /// Sets the content root of relative to the <paramref name="solutionRelativePath" />. /// </summary> /// <param name="builder">The <see cref="IWebHostBuilder"/>.</param> /// <param name="solutionRelativePath">The directory of the solution file.</param> /// <param name="applicationBasePath">The root of the app's directory.</param> /// <param name="solutionNames">The names of the solution files to make the content root relative to. If empty, defaults to *.sln and *.slnx.</param> /// <returns>The <see cref="IWebHostBuilder"/>.</returns> [SuppressMessage("ApiDesign", "RS0027:Public API with optional parameter(s) should have the most parameters amongst its public overloads", Justification = "Required to maintain compatibility")] public static IWebHostBuilder UseSolutionRelativeContentRoot( this IWebHostBuilder builder, string solutionRelativePath, string applicationBasePath, ReadOnlySpan<string> solutionNames = default) { ArgumentNullException.ThrowIfNull(solutionRelativePath); ArgumentNullException.ThrowIfNull(applicationBasePath); if (solutionNames.IsEmpty) { solutionNames = _defaultSolutionNames; } var directoryInfo = new DirectoryInfo(applicationBasePath); do { foreach (var solutionName in solutionNames) { var solutionPath = Directory.EnumerateFiles(directoryInfo.FullName, solutionName).FirstOrDefault(); if (solutionPath != null) { builder.UseContentRoot(Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath))); return builder; } } directoryInfo = directoryInfo.Parent; } while (directoryInfo is not null); throw new InvalidOperationException($"Solution root could not be located using application root {applicationBasePath}."); } #pragma warning disable CS0612 // Type or member is obsolete private sealed class ConfigureTestServicesStartupConfigureServicesFilter : IStartupConfigureServicesFilter #pragma warning restore CS0612 // Type or member is obsolete { private readonly Action<IServiceCollection> _servicesConfiguration; public ConfigureTestServicesStartupConfigureServicesFilter(Action<IServiceCollection> servicesConfiguration) { ArgumentNullException.ThrowIfNull(servicesConfiguration); _servicesConfiguration = servicesConfiguration; } public Action<IServiceCollection> ConfigureServices(Action<IServiceCollection> next) => serviceCollection => { next(serviceCollection); _servicesConfiguration(serviceCollection); }; } #pragma warning disable CS0612 // Type or member is obsolete private sealed class ConfigureTestServicesStartupConfigureContainerFilter<TContainer> : IStartupConfigureContainerFilter<TContainer> #pragma warning restore CS0612 // Type or member is obsolete { private readonly Action<TContainer> _servicesConfiguration; public ConfigureTestServicesStartupConfigureContainerFilter(Action<TContainer> containerConfiguration) { ArgumentNullException.ThrowIfNull(containerConfiguration); _servicesConfiguration = containerConfiguration; } public Action<TContainer> ConfigureContainer(Action<TContainer> next) => containerBuilder => { next(containerBuilder); _servicesConfiguration(containerBuilder); }; } }