Add docs for Session, Diagnostics.EntityFrameworkCore, MiddlewareAnalysis and HeaderPropagation (#26720)

This commit is contained in:
Stephen Halter 2020-10-09 20:38:45 -07:00 committed by GitHub
parent d694724475
commit ff6d1986ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 171 additions and 13 deletions

View File

@ -7,6 +7,10 @@ using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Http
{
/// <summary>
/// Stores user data while the user browses a web application. Session state uses a store maintained by the application
/// to persist data across requests from a client. The session data is backed by a cache and considered ephemeral data.
/// </summary>
public interface ISession
{
/// <summary>

View File

@ -3,8 +3,14 @@
namespace Microsoft.AspNetCore.Http.Features
{
/// <summary>
/// Provides access to the <see cref="ISession"/> for the current request.
/// </summary>
public interface ISessionFeature
{
/// <summary>
/// The <see cref="ISession"/> for the current request.
/// </summary>
ISession Session { get; set; }
}
}

View File

@ -16,17 +16,33 @@ using Microsoft.Extensions.Options;
#nullable enable
namespace Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
{
/// <summary>
/// An <see cref="IDeveloperPageExceptionFilter"/> that captures database-related exceptions that can be resolved by using Entity Framework migrations.
/// When these exceptions occur, an HTML response with details about possible actions to resolve the issue is generated.
/// </summary>
/// <remarks>
/// This should only be enabled in the Development environment.
/// </remarks>
public sealed class DatabaseDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter
{
private readonly ILogger _logger;
private readonly DatabaseErrorPageOptions _options;
/// <summary>
/// Initializes a new instance of <see cref="DatabaseDeveloperPageExceptionFilter"/>.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/>.</param>
/// <param name="options">The <see cref="IOptions{DatabaseErrorPageOptions}"/>.</param>
public DatabaseDeveloperPageExceptionFilter(ILogger<DatabaseDeveloperPageExceptionFilter> logger, IOptions<DatabaseErrorPageOptions> options)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_options = options?.Value ?? throw new ArgumentNullException(nameof(options));
}
/// <summary>
/// Handle <see cref="DbException"/> errors and output an HTML response with additional details.
/// </summary>
/// <inheritdoc />
public async Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next)
{
if (!(errorContext.Exception is DbException))

View File

@ -15,10 +15,14 @@ namespace Microsoft.Extensions.DependencyInjection
public static class DatabaseDeveloperPageExceptionFilterServiceExtensions
{
/// <summary>
/// Add response caching services.
/// In combination with UseDeveloperExceptionPage, this captures database-related exceptions that can be resolved by using Entity Framework migrations.
/// When these exceptions occur, an HTML response with details about possible actions to resolve the issue is generated.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
/// <returns></returns>
/// <remarks>
/// This should only be enabled in the Development environment.
/// </remarks>
public static IServiceCollection AddDatabaseDeveloperPageExceptionFilter(this IServiceCollection services)
{
if (services == null)

View File

@ -3,7 +3,7 @@
<PropertyGroup>
<Description>ASP.NET Core middleware for Entity Framework Core error pages. Use this middleware to detect and diagnose errors with Entity Framework Core migrations.</Description>
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;diagnostics;entityframeworkcore</PackageTags>
</PropertyGroup>

View File

@ -17,6 +17,9 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
/// <remarks>
/// This should only be enabled in the Development environment.
/// </remarks>
public static IApplicationBuilder UseDeveloperExceptionPage(this IApplicationBuilder app)
{
if (app == null)
@ -33,6 +36,9 @@ namespace Microsoft.AspNetCore.Builder
/// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
/// <param name="options">A <see cref="DeveloperExceptionPageOptions"/> that specifies options for the middleware.</param>
/// <returns>A reference to the <paramref name="app"/> after the operation has completed.</returns>
/// <remarks>
/// This should only be enabled in the Development environment.
/// </remarks>
public static IApplicationBuilder UseDeveloperExceptionPage(
this IApplicationBuilder app,
DeveloperExceptionPageOptions options)

View File

@ -8,6 +8,9 @@ using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// <see cref="IApplicationBuilder"/> extension methods for <see cref="HeaderPropagationMiddleware"/> which propagates request headers to an <see cref="HttpClient"/>.
/// </summary>
public static class HeaderPropagationApplicationBuilderExtensions
{
private static readonly string _unableToFindServices = string.Format(

View File

@ -7,6 +7,9 @@ using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// <see cref="IHttpClientBuilder"/> extension methods for <see cref="HeaderPropagationMiddleware"/> which propagates request headers to an <see cref="System.Net.Http.HttpClient"/>.
/// </summary>
public static class HeaderPropagationHttpClientBuilderExtensions
{
/// <summary>

View File

@ -8,6 +8,9 @@ using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// <see cref="IServiceCollection"/> extension methods for <see cref="HeaderPropagationMiddleware"/> which propagates request headers to an <see cref="System.Net.Http.HttpClient"/>.
/// </summary>
public static class HeaderPropagationServiceCollectionExtensions
{
/// <summary>

View File

@ -12,7 +12,7 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNetCore.HeaderPropagation
{
/// <summary>
/// A Middleware for propagating headers to a <see cref="HttpClient"/>.
/// A Middleware for propagating headers to an <see cref="HttpClient"/>.
/// </summary>
public class HeaderPropagationMiddleware
{
@ -20,6 +20,14 @@ namespace Microsoft.AspNetCore.HeaderPropagation
private readonly HeaderPropagationOptions _options;
private readonly HeaderPropagationValues _values;
/// <summary>
/// Initializes a new instance of <see cref="HeaderPropagationMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="options">The <see cref="IOptions{HeaderPropagationOptions}"/>.</param>
/// <param name="values">
/// The <see cref="HeaderPropagationValues"/> that stores the request headers to be propagated in an <see cref="System.Threading.AsyncLocal{T}"/>
/// </param>
public HeaderPropagationMiddleware(RequestDelegate next, IOptions<HeaderPropagationOptions> options, HeaderPropagationValues values)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
@ -33,6 +41,10 @@ namespace Microsoft.AspNetCore.HeaderPropagation
_values = values ?? throw new ArgumentNullException(nameof(values));
}
/// <summary>
/// Executes the middleware that stores the request headers to be propagated in using <see cref="HeaderPropagationValues"/>.
/// </summary>
/// <param name="context">The <see cref="HttpContext"/> for the current request.</param>
public Task Invoke(HttpContext context)
{
// We need to intialize the headers because the message handler will use this to detect misconfiguration.

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core middleware to propagate HTTP headers from the incoming request to the outgoing HTTP Client requests</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;httpclient</PackageTags>
</PropertyGroup>

View File

@ -9,10 +9,18 @@ using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.MiddlewareAnalysis
{
/// <summary>
/// An <see cref="IApplicationBuilder"/> decorator used by <see cref="AnalysisStartupFilter"/>
/// to add <see cref="AnalysisMiddleware"/> before and after each other middleware in the pipeline.
/// </summary>
public class AnalysisBuilder : IApplicationBuilder
{
private const string NextMiddlewareName = "analysis.NextMiddlewareName";
/// <summary>
/// Initializes a new instance of <see cref="AnalysisBuilder"/>.
/// </summary>
/// <param name="inner">The <see cref="IApplicationBuilder"/> to decorate.</param>
public AnalysisBuilder(IApplicationBuilder inner)
{
InnerBuilder = inner;
@ -20,22 +28,26 @@ namespace Microsoft.AspNetCore.MiddlewareAnalysis
private IApplicationBuilder InnerBuilder { get; }
/// <inheritdoc />
public IServiceProvider ApplicationServices
{
get { return InnerBuilder.ApplicationServices; }
set { InnerBuilder.ApplicationServices = value; }
}
/// <inheritdoc />
public IDictionary<string, object> Properties
{
get { return InnerBuilder.Properties; }
}
/// <inheritdoc />
public IFeatureCollection ServerFeatures
{
get { return InnerBuilder.ServerFeatures;}
}
/// <inheritdoc />
public RequestDelegate Build()
{
// Add one maker at the end before the default 404 middleware (or any fancy Join middleware).
@ -43,11 +55,13 @@ namespace Microsoft.AspNetCore.MiddlewareAnalysis
.Build();
}
/// <inheritdoc />
public IApplicationBuilder New()
{
return new AnalysisBuilder(InnerBuilder.New());
}
/// <inheritdoc />
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
{
string middlewareName = string.Empty; // UseMiddleware doesn't work with null params.

View File

@ -8,6 +8,10 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.MiddlewareAnalysis
{
/// <summary>
/// Middleware that is inserted before and after each other middleware in the pipeline by <see cref="AnalysisBuilder"/>
/// to log to a <see cref="DiagnosticSource"/> when other middleware starts, finishes and throws.
/// </summary>
public class AnalysisMiddleware
{
private readonly Guid _instanceId = Guid.NewGuid();
@ -15,6 +19,15 @@ namespace Microsoft.AspNetCore.MiddlewareAnalysis
private readonly DiagnosticSource _diagnostics;
private readonly string _middlewareName;
/// <summary>
/// Initializes a new instance of <see cref="AnalysisMiddleware"/>.
/// </summary>
/// <param name="next">The next middleware in the pipeline.</param>
/// <param name="diagnosticSource">The <see cref="DiagnosticSource"/> to log when other middleware starts, finishes and throws.</param>
/// <param name="middlewareName">
/// The name of the next middleware in the pipeline. This name is typically retrieved from <see cref="Builder.IApplicationBuilder.Properties"/>
/// using the "analysis.NextMiddlewareName" key.
/// </param>
public AnalysisMiddleware(RequestDelegate next, DiagnosticSource diagnosticSource, string middlewareName)
{
_next = next;
@ -26,6 +39,10 @@ namespace Microsoft.AspNetCore.MiddlewareAnalysis
_middlewareName = middlewareName;
}
/// <summary>
/// Executes the middleware that logs to a <see cref="DiagnosticSource"/> when the next middleware starts, finishes and throws.
/// </summary>
/// <param name="httpContext">The <see cref="HttpContext"/> for the current request.</param>
public async Task Invoke(HttpContext httpContext)
{
var startTimestamp = Stopwatch.GetTimestamp();

View File

@ -14,7 +14,8 @@ namespace Microsoft.Extensions.DependencyInjection
public static class AnalysisServiceCollectionExtensions
{
/// <summary>
/// Adds diagnostic services to the specified <see cref="IServiceCollection" />.
/// Adds diagnostic services to the specified <see cref="IServiceCollection" /> by logging to
/// a <see cref="System.Diagnostics.DiagnosticSource"/> when middleware starts, finishes and throws.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
/// <returns>The <see cref="IServiceCollection"/> so that additional calls can be chained.</returns>

View File

@ -7,8 +7,17 @@ using Microsoft.AspNetCore.Hosting;
namespace Microsoft.AspNetCore.MiddlewareAnalysis
{
/// <summary>
/// An <see cref="IStartupFilter"/> that configures the middleware pipeline to log to a <see cref="System.Diagnostics.DiagnosticSource"/>
/// when middleware starts, finishes and throws.
/// </summary>
public class AnalysisStartupFilter : IStartupFilter
{
/// <summary>
/// Wraps the <see cref="IApplicationBuilder"/> with <see cref="AnalysisBuilder"/> and directly adds
/// <see cref="AnalysisMiddleware"/> to the end of the middleware pipeline.
/// </summary>
/// <inheritdoc />
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>

View File

@ -1,9 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core middleware for analyzing middleware in the request pipeline with System.Diagnostics.DiagnosticSource.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;diagnostics</PackageTags>
</PropertyGroup>

View File

@ -14,6 +14,9 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Session
{
/// <summary>
/// An <see cref="ISession"/> backed by an <see cref="IDistributedCache"/>.
/// </summary>
public class DistributedSession : ISession
{
private const int IdByteCount = 16;
@ -35,6 +38,23 @@ namespace Microsoft.AspNetCore.Session
private string _sessionId;
private byte[] _sessionIdBytes;
/// <summary>
/// Initializes a new instance of <see cref="DistributedSession"/>.
/// </summary>
/// <param name="cache">The <see cref="IDistributedCache"/> used to store the session data.</param>
/// <param name="sessionKey">A unique key used to lookup the session.</param>
/// <param name="idleTimeout">How long the session can be inactive (e.g. not accessed) before it will expire.</param>
/// <param name="ioTimeout">
/// The maximum amount of time <see cref="LoadAsync(CancellationToken)"/> and <see cref="CommitAsync(CancellationToken)"/> are allowed take.
/// </param>
/// <param name="tryEstablishSession">
/// A callback invoked during <see cref="Set(string, byte[])"/> to verify that modifying the session is currently valid.
/// If the callback returns <see langword="false"/>, <see cref="Set(string, byte[])"/> throws an <see cref="InvalidOperationException"/>.
/// <see cref="SessionMiddleware"/> provides a callback that returns <see langword="false"/> if the session was not established
/// prior to sending the response.
/// </param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <param name="isNewSessionKey"><see langword="true"/> if establishing a new session; <see langword="false"/> if resuming a session.</param>
public DistributedSession(
IDistributedCache cache,
string sessionKey,
@ -74,6 +94,7 @@ namespace Microsoft.AspNetCore.Session
_isNewSessionKey = isNewSessionKey;
}
/// <inheritdoc />
public bool IsAvailable
{
get
@ -83,6 +104,7 @@ namespace Microsoft.AspNetCore.Session
}
}
/// <inheritdoc />
public string Id
{
get
@ -109,6 +131,7 @@ namespace Microsoft.AspNetCore.Session
}
}
/// <inheritdoc/>
public IEnumerable<string> Keys
{
get
@ -118,12 +141,14 @@ namespace Microsoft.AspNetCore.Session
}
}
/// <inheritdoc />
public bool TryGetValue(string key, out byte[] value)
{
Load();
return _store.TryGetValue(new EncodedKey(key), out value);
}
/// <inheritdoc />
public void Set(string key, byte[] value)
{
if (value == null)
@ -151,12 +176,14 @@ namespace Microsoft.AspNetCore.Session
}
}
/// <inheritdoc />
public void Remove(string key)
{
Load();
_isModified |= _store.Remove(new EncodedKey(key));
}
/// <inheritdoc />
public void Clear()
{
Load();
@ -196,9 +223,10 @@ namespace Microsoft.AspNetCore.Session
}
}
// This will throw if called directly and a failure occurs. The user is expected to handle the failures.
/// <inheritdoc />
public async Task LoadAsync(CancellationToken cancellationToken = default)
{
// This will throw if called directly and a failure occurs. The user is expected to handle the failures.
if (!_loaded)
{
using (var timeout = new CancellationTokenSource(_ioTimeout))
@ -232,6 +260,7 @@ namespace Microsoft.AspNetCore.Session
}
}
/// <inheritdoc />
public async Task CommitAsync(CancellationToken cancellationToken = default)
{
using (var timeout = new CancellationTokenSource(_ioTimeout))
@ -419,6 +448,5 @@ namespace Microsoft.AspNetCore.Session
}
return output;
}
}
}
}

View File

@ -8,11 +8,19 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.AspNetCore.Session
{
/// <summary>
/// An <see cref="ISessionStore"/> backed by an <see cref="IDistributedCache"/>.
/// </summary>
public class DistributedSessionStore : ISessionStore
{
private readonly IDistributedCache _cache;
private readonly ILoggerFactory _loggerFactory;
/// <summary>
/// Initializes a new instance of <see cref="DistributedSessionStore"/>.
/// </summary>
/// <param name="cache">The <see cref="IDistributedCache"/> used to store the session data.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public DistributedSessionStore(IDistributedCache cache, ILoggerFactory loggerFactory)
{
if (cache == null)
@ -29,6 +37,7 @@ namespace Microsoft.AspNetCore.Session
_loggerFactory = loggerFactory;
}
/// <inheritdoc />
public ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey)
{
if (string.IsNullOrEmpty(sessionKey))

View File

@ -6,8 +6,28 @@ using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Session
{
/// <summary>
/// Storage for sessions that maintain user data while the user browses a web application.
/// </summary>
public interface ISessionStore
{
/// <summary>
/// Create a new or resume an <see cref="ISession"/>.
/// </summary>
/// <param name="sessionKey">A unique key used to lookup the session.</param>
/// <param name="idleTimeout">How long the session can be inactive (e.g. not accessed) before it will expire.</param>
/// <param name="ioTimeout">
/// The maximum amount of time <see cref="ISession.LoadAsync(System.Threading.CancellationToken)"/> and
/// <see cref="ISession.CommitAsync(System.Threading.CancellationToken)"/> are allowed take.
/// </param>
/// <param name="tryEstablishSession">
/// A callback invoked during <see cref="ISession.Set(string, byte[])"/> to verify that modifying the session is currently valid.
/// If the callback returns <see langword="false"/>, <see cref="ISession.Set(string, byte[])"/> should throw an <see cref="InvalidOperationException"/>.
/// <see cref="SessionMiddleware"/> provides a callback that returns <see langword="false"/> if the session was not established
/// prior to sending the response.
/// </param>
/// <param name="isNewSessionKey"><see langword="true"/> if establishing a new session; <see langword="false"/> if resuming a session.</param>
/// <returns>The <see cref="ISession"/> that was created or resumed.</returns>
ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);
}
}
}

View File

@ -4,7 +4,7 @@
<Description>ASP.NET Core session state middleware.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<IsAspNetCoreApp>true</IsAspNetCoreApp>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore;session;sessionstate</PackageTags>

View File

@ -6,8 +6,10 @@ using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Session
{
/// <inheritdoc />
public class SessionFeature : ISessionFeature
{
/// <inheritdoc />
public ISession Session { get; set; }
}
}

View File

@ -3,6 +3,7 @@
<PropertyGroup>
<Description>Helpers for building single-page applications on ASP.NET MVC Core.</Description>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<NoWarn>$(NoWarn.Replace('1591', ''))</NoWarn>
</PropertyGroup>
<ItemGroup>