Permalink
Browse files

Merge pull request #253 from martincostello/AspNet-Core-300-Prep

ASP.NET Core 3.0 preparation
  • Loading branch information...
martincostello committed Feb 10, 2019
2 parents c0d88a2 + cc5dd85 commit 4668669950d10ba0c11796e90657833d4921eaca
@@ -30,7 +30,7 @@
<LangVersion>latest</LangVersion>
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<NeutralLanguage>en-US</NeutralLanguage>
<NoWarn>$(NoWarn);CA1054;CA1056;CA1707;CA1720;CA2227;CA2234</NoWarn>
<NoWarn>$(NoWarn);CA1054;CA1056;CA1707;CA1720;CA2227;CA2234;MSB3030</NoWarn>
<PackageIconUrl>https://martincostello.com/favicon.ico</PackageIconUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/martincostello/website</PackageProjectUrl>
@@ -1,4 +1,4 @@
// Copyright (c) Martin Costello, 2016. All rights reserved.
// Copyright (c) Martin Costello, 2016. All rights reserved.
// Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information.

namespace MartinCostello.Website.Extensions
@@ -29,7 +29,7 @@ public static class IApplicationBuilderExtensions
this IApplicationBuilder value,
IHostingEnvironment environment,
IConfiguration config,
IOptionsSnapshot<SiteOptions> options)
IOptions<SiteOptions> options)
{
return value.UseMiddleware<CustomHttpHeadersMiddleware>(environment, config, options);
}
@@ -32,9 +32,9 @@ public sealed class CustomHttpHeadersMiddleware
private readonly IConfiguration _config;

/// <summary>
/// The options snapshot to use. This field is read-only.
/// The options to use. This field is read-only.
/// </summary>
private readonly IOptionsSnapshot<SiteOptions> _options;
private readonly IOptions<SiteOptions> _options;

/// <summary>
/// The current <c>Content-Security-Policy</c> HTTP response header value. This field is read-only.
@@ -72,7 +72,7 @@ public sealed class CustomHttpHeadersMiddleware
RequestDelegate next,
IHostingEnvironment environment,
IConfiguration config,
IOptionsSnapshot<SiteOptions> options)
IOptions<SiteOptions> options)
{
_next = next;
_config = config;
@@ -14,6 +14,7 @@ namespace MartinCostello.Website.Services
using Api.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Options;

/// <summary>
@@ -53,12 +54,12 @@ public class ToolsService : IToolsService
/// <summary>
/// Initializes a new instance of the <see cref="ToolsService"/> class.
/// </summary>
/// <param name="context">The current HTTP context.</param>
/// <param name="contextAccessor">The <see cref="IHttpContextAccessor"/> to use.</param>
/// <param name="options">The site options to use.</param>
public ToolsService(HttpContext context, SiteOptions options)
public ToolsService(IHttpContextAccessor contextAccessor, IOptions<SiteOptions> options)
{
_apiUri = options?.ExternalLinks?.Api;
_traceId = context.TraceIdentifier;
_apiUri = options?.Value?.ExternalLinks?.Api;
_traceId = contextAccessor.HttpContext.TraceIdentifier;
}

/// <inheritdoc/>
@@ -41,28 +41,34 @@ public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironm
/// <summary>
/// Gets the current configuration.
/// </summary>
public IConfiguration Configuration { get; }
private IConfiguration Configuration { get; }

/// <summary>
/// Gets the current hosting environment.
/// </summary>
public IHostingEnvironment HostingEnvironment { get; }
private IHostingEnvironment HostingEnvironment { get; }

/// <summary>
/// Gets or sets the service provider.
/// </summary>
public IServiceProvider ServiceProvider { get; set; }
private IServiceProvider ServiceProvider { get; set; }

/// <summary>
/// Configures the application.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> to use.</param>
/// <param name="applicationLifetime">The <see cref="IApplicationLifetime"/> to use.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to use.</param>
/// <param name="options">The snapshot of <see cref="SiteOptions"/> to use.</param>
public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider, IOptionsSnapshot<SiteOptions> options)
public void Configure(
IApplicationBuilder app,
IApplicationLifetime applicationLifetime,
IServiceProvider serviceProvider,
IOptions<SiteOptions> options)
{
ServiceProvider = serviceProvider;
ServiceProvider = serviceProvider.CreateScope().ServiceProvider;

applicationLifetime.ApplicationStopped.Register(OnApplicationStopped);
app.UseCustomHttpHeaders(HostingEnvironment, Configuration, options);

if (HostingEnvironment.IsDevelopment())
@@ -140,10 +146,9 @@ public void ConfigureServices(IServiceCollection services)
.AddResponseCompression();

services.AddSingleton<IClock>((_) => SystemClock.Instance);

services.AddScoped((p) => p.GetRequiredService<IHttpContextAccessor>().HttpContext);
services.AddScoped((p) => p.GetRequiredService<IOptionsSnapshot<SiteOptions>>().Value);
services.AddScoped<IToolsService, ToolsService>();
services.AddSingleton<IToolsService, ToolsService>();
services.AddHttpContextAccessor();
services.AddScoped((p) => p.GetRequiredService<IOptions<SiteOptions>>().Value);
}

/// <summary>
@@ -251,8 +256,19 @@ private CookiePolicyOptions CreateCookiePolicy()
/// The <see cref="CookieSecurePolicy"/> to use for the application.
/// </returns>
private CookieSecurePolicy CookiePolicy()
=> HostingEnvironment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always;

/// <summary>
/// Handles the application being stopped.
/// </summary>
private void OnApplicationStopped()
{
return HostingEnvironment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always;
Serilog.Log.CloseAndFlush();

if (ServiceProvider is IDisposable disposable)
{
disposable.Dispose();
}
}
}
}
@@ -8,11 +8,7 @@ namespace MartinCostello.Website.Integration
using System.Net.Http;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using MartinCostello.Logging.XUnit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;

/// <summary>
/// A test fixture representing an HTTP server hosting the application. This class cannot be inherited.
@@ -31,7 +27,6 @@ public HttpServerFixture()
ClientOptions.BaseAddress = FindFreeServerAddress();

var builder = CreateWebHostBuilder()
.UseSolutionRelativeContentRoot("src/Website")
.UseUrls(ClientOptions.BaseAddress.ToString())
.UseKestrel(
(p) => p.ConfigureHttpsDefaults(
@@ -48,6 +43,9 @@ public HttpServerFixture()
/// </summary>
public Uri ServerAddress => ClientOptions.BaseAddress;

/// <inheritdoc />
public override IServiceProvider Services => _webHost?.Services;

/// <summary>
/// Creates an <see cref="HttpClient"/> to communicate with the application.
/// </summary>
@@ -78,14 +76,6 @@ public HttpClient CreateHttpClient()
return client;
}

/// <inheritdoc />
public override void ClearOutputHelper()
=> _webHost.Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = null;

/// <inheritdoc />
public override void SetOutputHelper(ITestOutputHelper value)
=> _webHost.Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = value;

/// <inheritdoc />
protected override void Dispose(bool disposing)
{
@@ -5,6 +5,8 @@ namespace MartinCostello.Website.Integration
{
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using MartinCostello.Logging.XUnit;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
@@ -27,30 +29,58 @@ public TestServerFixture()
ClientOptions.AllowAutoRedirect = false;
ClientOptions.BaseAddress = new Uri("https://localhost");

// HACK Force HTTP server startup
using (CreateDefaultClient())
{
}
EnsureStarted();
}

/// <summary>
/// Gets the <see cref="IServiceProvider"/> in use.
/// </summary>
public virtual IServiceProvider Services => Server?.Host?.Services;

/// <summary>
/// Clears the current <see cref="ITestOutputHelper"/>.
/// </summary>
public virtual void ClearOutputHelper()
=> Server.Host.Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = null;
{
if (Services != null)
{
Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = null;
}
}

/// <summary>
/// Sets the <see cref="ITestOutputHelper"/> to use.
/// </summary>
/// <param name="value">The <see cref="ITestOutputHelper"/> to use.</param>
public virtual void SetOutputHelper(ITestOutputHelper value)
=> Server.Host.Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = value;
{
EnsureStarted();
Services.GetRequiredService<ITestOutputHelperAccessor>().OutputHelper = value;
}

/// <inheritdoc />
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(ConfigureTests)
.ConfigureLogging((loggingBuilder) => loggingBuilder.ClearProviders().AddXUnit());
.ConfigureLogging((loggingBuilder) => loggingBuilder.ClearProviders().AddXUnit())
.UseContentRoot(GetApplicationContentRootPath());
}

/// <summary>
/// Gets the content root path to use for the application.
/// </summary>
/// <returns>
/// The content root path to use for the application.
/// </returns>
protected string GetApplicationContentRootPath()
{
var attribute = GetTestAssemblies()
.SelectMany((p) => p.GetCustomAttributes<WebApplicationFactoryContentRootAttribute>())
.Where((p) => string.Equals(p.Key, "Website", StringComparison.OrdinalIgnoreCase))
.OrderBy((p) => p.Priority)
.First();

return attribute.ContentRootPath;
}

private static void ConfigureTests(IConfigurationBuilder builder)
@@ -66,5 +96,13 @@ private static void ConfigureTests(IConfigurationBuilder builder)
.AddJsonFile(fullPath)
.AddEnvironmentVariables();
}

private void EnsureStarted()
{
// HACK Force HTTP server startup
using (CreateDefaultClient())
{
}
}
}
}
@@ -13,6 +13,12 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Website\Website.csproj" />
<WebApplicationFactoryContentRootAttribute
Include="Website"
AssemblyName="Website"
ContentRootPath="$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)../../src/Website'))"
ContentRootTest="Website.csproj"
Priority="-1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.1.0" />

0 comments on commit 4668669

Please sign in to comment.