Skip to content

Commit

Permalink
Refactor to use a default internal container and dependency resolver …
Browse files Browse the repository at this point in the history
…instead of static singletons
  • Loading branch information
commonsensesoftware committed Sep 12, 2023
1 parent e29d885 commit f680ece
Show file tree
Hide file tree
Showing 19 changed files with 160 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public async Task options_should_return_expected_headers()
configuration.AddApiVersioning(
options =>
{
options.ReportApiVersions = true;
options.Policies.Sunset( "VersionedMetadata" )
.Link( "policies" )
.Title( "Versioning Policy" )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public IDocumentationProvider DocumentationProvider
/// <value>The configured <see cref="ISunsetPolicyManager">sunset policy manager</see>.</value>
protected ISunsetPolicyManager SunsetPolicyManager
{
get => sunsetPolicyManager ??= Configuration.DependencyResolver.GetSunsetPolicyManager();
get => sunsetPolicyManager ??= Configuration.GetSunsetPolicyManager();
set => sunsetPolicyManager = value;
}

Expand Down Expand Up @@ -227,7 +227,7 @@ protected virtual ApiDescriptionGroupCollection InitializeApiDescriptions()
}

var routes = FlattenRoutes( Configuration.Routes ).ToArray();
var policyManager = Configuration.DependencyResolver.GetSunsetPolicyManager();
var policyManager = Configuration.GetSunsetPolicyManager();

foreach ( var apiVersion in FlattenApiVersions( controllerMappings ) )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public ApiVersion? RequestedApiVersion
return apiVersion;
}

var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser();
var parser = request.GetConfiguration().GetApiVersionParser();

try
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ namespace Asp.Versioning;
/// </content>
public partial class DefaultApiVersionReporter
{
private static DefaultApiVersionReporter? instance;

internal static IReportApiVersions GetOrCreate( ISunsetPolicyManager sunsetPolicyManager ) =>
instance ??= new( sunsetPolicyManager );

private static void AddApiVersionHeader( HttpResponseHeaders headers, string headerName, IReadOnlyList<ApiVersion> versions )
{
if ( versions.Count == 0 || headers.Contains( headerName ) )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.

namespace Asp.Versioning.Dependencies;

using Asp.Versioning;
using Asp.Versioning.Conventions;
using System.ComponentModel.Design;
using System.Web.Http.Dependencies;

internal sealed class DefaultContainer : IDependencyResolver, IDependencyScope
{
private readonly ServiceContainer container = new();
private bool disposed;

internal DefaultContainer()
{
container.AddService( typeof( ApiVersioningOptions ), static ( sc, t ) => new ApiVersioningOptions() );
container.AddService( typeof( IApiVersionParser ), static ( sc, t ) => ApiVersionParser.Default );
container.AddService( typeof( IControllerNameConvention ), static ( sc, t ) => ControllerNameConvention.Default );
container.AddService( typeof( IProblemDetailsFactory ), static ( sc, t ) => new ProblemDetailsFactory() );
container.AddService( typeof( ISunsetPolicyManager ), NewSunsetPolicyManager );
container.AddService( typeof( IReportApiVersions ), NewApiVersionReporter );
}

public ApiVersioningOptions ApiVersioningOptions
{
get => GetApiVersioningOptions( container );
set
{
container.RemoveService( typeof( ApiVersioningOptions ) );
container.AddService( typeof( ApiVersioningOptions ), value );
}
}

public void Replace( Type serviceType, ServiceCreatorCallback activator )
{
container.RemoveService( serviceType );
container.AddService( serviceType, activator );
}

public IDependencyScope BeginScope() => this;

public void Dispose()
{
if ( disposed )
{
return;
}

disposed = true;
container.Dispose();
}

public object GetService( Type serviceType ) => container.GetService( serviceType );

public IEnumerable<object> GetServices( Type serviceType )
{
var service = container.GetService( serviceType );

if ( service is not null )
{
yield return service;
}
}

private static ApiVersioningOptions GetApiVersioningOptions( IServiceProvider serviceProvider ) =>
(ApiVersioningOptions) serviceProvider.GetService( typeof( ApiVersioningOptions ) );

private static ISunsetPolicyManager NewSunsetPolicyManager( IServiceProvider serviceProvider, Type type ) =>
new SunsetPolicyManager( GetApiVersioningOptions( serviceProvider ) );

private static IReportApiVersions NewApiVersionReporter( IServiceProvider serviceProvider, Type type )
{
var options = GetApiVersioningOptions( serviceProvider );

if ( options.ReportApiVersions )
{
var sunsetPolicyManager = (ISunsetPolicyManager) serviceProvider.GetService( typeof( ISunsetPolicyManager ) );
return new DefaultApiVersionReporter( sunsetPolicyManager );
}

return new DoNotReportApiVersions();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,39 @@
namespace Asp.Versioning;

using Asp.Versioning.Conventions;
using System.Globalization;
using System.Web.Http;
using System.Web.Http.Dependencies;

internal static class DependencyResolverExtensions
{
internal static TService? GetService<TService>( this IDependencyResolver resolver ) => (TService) resolver.GetService( typeof( TService ) );

internal static IApiVersionParser GetApiVersionParser( this IDependencyResolver resolver ) =>
resolver.GetService<IApiVersionParser>() ?? ApiVersionParser.Default;

internal static IReportApiVersions GetApiVersionReporter( this IDependencyResolver resolver ) =>
resolver.GetService<IReportApiVersions>() ?? DefaultApiVersionReporter.GetOrCreate( resolver.GetSunsetPolicyManager() );

internal static IControllerNameConvention GetControllerNameConvention( this IDependencyResolver resolver ) =>
resolver.GetService<IControllerNameConvention>() ?? ControllerNameConvention.Default;

internal static IProblemDetailsFactory GetProblemDetailsFactory( this IDependencyResolver resolver ) =>
resolver.GetService<IProblemDetailsFactory>() ?? ProblemDetailsFactory.Default;

internal static ISunsetPolicyManager GetSunsetPolicyManager( this IDependencyResolver resolver ) =>
resolver.GetService<ISunsetPolicyManager>() ?? SunsetPolicyManager.Default;
internal static TService? GetService<TService>( this IDependencyResolver resolver ) =>
(TService) resolver.GetService( typeof( TService ) );

internal static TService GetRequiredService<TService>( this IDependencyResolver resolver )
{
var service = resolver.GetService<TService>();
var message = string.Format( CultureInfo.CurrentCulture, SR.NoServiceRegistered, typeof( TService ) );
return service ?? throw new InvalidOperationException( message );
}

internal static IApiVersionParser GetApiVersionParser( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService<IApiVersionParser>() ??
configuration.ApiVersioningServices().GetRequiredService<IApiVersionParser>();

internal static IReportApiVersions GetApiVersionReporter( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService<IReportApiVersions>() ??
configuration.ApiVersioningServices().GetRequiredService<IReportApiVersions>();

internal static IControllerNameConvention GetControllerNameConvention( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService<IControllerNameConvention>() ??
configuration.ApiVersioningServices().GetRequiredService<IControllerNameConvention>();

internal static IProblemDetailsFactory GetProblemDetailsFactory( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService<IProblemDetailsFactory>() ??
configuration.ApiVersioningServices().GetRequiredService<IProblemDetailsFactory>();

internal static ISunsetPolicyManager GetSunsetPolicyManager( this HttpConfiguration configuration ) =>
configuration.DependencyResolver.GetService<ISunsetPolicyManager>() ??
configuration.ApiVersioningServices().GetRequiredService<ISunsetPolicyManager>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ private static void ApplyImplicitConventions( HttpControllerDescriptor controlle
}

var actions = mapping.SelectMany( g => g );
var namingConvention = controller.Configuration.DependencyResolver.GetControllerNameConvention();
var namingConvention = controller.Configuration.GetControllerNameConvention();
var name = namingConvention.GroupName( controller.ControllerName );
var metadata = new ApiVersionMetadata( implicitVersionModel, implicitVersionModel, name );

Expand Down Expand Up @@ -344,7 +344,7 @@ private static void ApplyCollatedModelsToActions(
HttpConfiguration configuration,
List<Tuple<HttpControllerDescriptor, HttpActionDescriptor, ApiVersionModel>> visitedActions )
{
var namingConvention = configuration.DependencyResolver.GetControllerNameConvention();
var namingConvention = configuration.GetControllerNameConvention();

for ( var i = 0; i < visitedActions.Count; i++ )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private Dictionary<string, ILookup<string, Type>> InitializeCache()
var services = configuration.Services;
var assembliesResolver = services.GetAssembliesResolver();
var typeResolver = services.GetHttpControllerTypeResolver();
var convention = configuration.DependencyResolver.GetControllerNameConvention();
var convention = configuration.GetControllerNameConvention();
var comparer = StringComparer.OrdinalIgnoreCase;

return typeResolver.GetControllerTypes( assembliesResolver )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ internal HttpResponseExceptionFactory( HttpRequestMessage request, ApiVersionMod

private ApiVersioningOptions Options => configuration.GetApiVersioningOptions();

private IProblemDetailsFactory ProblemDetails => configuration.DependencyResolver.GetProblemDetailsFactory();
private IProblemDetailsFactory ProblemDetails => configuration.GetProblemDetailsFactory();

private ITraceWriter TraceWriter => configuration.Services.GetTraceWriter() ?? NullTraceWriter.Instance;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ namespace Asp.Versioning;

internal sealed class DoNotReportApiVersions : IReportApiVersions
{
private static DoNotReportApiVersions? instance;

private DoNotReportApiVersions() { }

internal static IReportApiVersions Instance => instance ??= new();

public ApiVersionMapping Mapping => Explicit | Implicit;

public void Report( HttpResponseMessage response, ApiVersionModel apiVersionModel ) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public ProblemDetails CreateProblemDetails(
return ProblemDetailsFactory.AddUnsupportedExtensions( request, status, problem, ApplyMessage );
}

return ProblemDetailsFactory.Default.CreateProblemDetails(
return ProblemDetailsFactory.NewProblemDetails(
request,
statusCode,
title,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@ namespace Asp.Versioning;

internal sealed class ProblemDetailsFactory : IProblemDetailsFactory
{
private static IProblemDetailsFactory? @default;

public static IProblemDetailsFactory Default
{
get => @default ??= new ProblemDetailsFactory();
set => @default = value;
}

public ProblemDetails CreateProblemDetails(
HttpRequestMessage request,
int? statusCode = null,
string? title = null,
string? type = null,
string? detail = null,
string? instance = null ) =>
NewProblemDetails( request, statusCode, title, type, detail, instance );

internal static ProblemDetails NewProblemDetails(
HttpRequestMessage request,
int? statusCode = null,
string? title = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,7 @@ public override void OnActionExecuted( HttpActionExecutedContext actionExecutedC

var context = actionExecutedContext.ActionContext;
var action = context.ActionDescriptor;
var reporter = reportApiVersions;

if ( reporter is null )
{
var dependencyResolver = context.ControllerContext.Configuration.DependencyResolver;
reporter = dependencyResolver.GetApiVersionReporter();
}

var reporter = reportApiVersions ?? context.ControllerContext.Configuration.GetApiVersionReporter();
var model = action.GetApiVersionMetadata().Map( reporter.Mapping );

response.RequestMessage ??= actionExecutedContext.Request;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public bool Match( HttpRequestMessage request, IHttpRoute route, string paramete
return !string.IsNullOrEmpty( value );
}

var parser = request.GetConfiguration().DependencyResolver.GetApiVersionParser();
var parser = request.GetConfiguration().GetApiVersionParser();
var properties = request.ApiVersionProperties();

properties.RouteParameter = parameterName;
Expand Down
9 changes: 9 additions & 0 deletions src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/AspNet/WebApi/src/Asp.Versioning.WebApi/SR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@
<data name="NoControllerSelected" xml:space="preserve">
<value>A controller was not selected for request URI '{0}' and API version '{1}'.</value>
</data>
<data name="NoServiceRegistered" xml:space="preserve">
<value>No service for type '{0}' has been registered.</value>
</data>
<data name="ResourceNotFound" xml:space="preserve">
<value>No HTTP resource was found that matches the request URI '{0}'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,10 @@ namespace Asp.Versioning;
public partial class SunsetPolicyManager
{
private readonly ApiVersioningOptions options;
private static ISunsetPolicyManager? @default;

/// <summary>
/// Initializes a new instance of the <see cref="SunsetPolicyManager"/> class.
/// </summary>
/// <param name="options">The associated <see cref="ApiVersioningOptions">API versioning options</see>.</param>
public SunsetPolicyManager( ApiVersioningOptions options ) => this.options = options;

internal static ISunsetPolicyManager Default
{
get => @default ?? new SunsetPolicyManager( new ApiVersioningOptions() );
set => @default = value;
}
}
Loading

0 comments on commit f680ece

Please sign in to comment.