diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Extensions/EndpointConventionBuilderExtensions.cs b/src/Finbuckle.MultiTenant.AspNetCore/Extensions/EndpointConventionBuilderExtensions.cs
new file mode 100644
index 00000000..a526d1d8
--- /dev/null
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Extensions/EndpointConventionBuilderExtensions.cs
@@ -0,0 +1,27 @@
+using Finbuckle.MultiTenant.AspNetCore.Routing;
+using Microsoft.AspNetCore.Builder;
+
+namespace Finbuckle.MultiTenant.AspNetCore.Extensions;
+
+public static class EndpointConventionBuilderExtensions
+{
+ private static readonly ExcludeFromMultiTenantResolutionAttribute s_excludeFromMultiTenantResolutionAttribute = new();
+
+ ///
+ /// Adds the to
+ /// for all endpoints produced by the .
+ ///
+ /// The .
+ /// A that can be used to further customize the endpoint.
+ public static TBuilder ExcludeFromMultiTenantResolution(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
+ => builder.WithMetadata(s_excludeFromMultiTenantResolutionAttribute);
+
+ ///
+ /// Adds the to
+ /// for all endpoints produced by the .
+ ///
+ /// The .
+ /// A that can be used to further customize the endpoint.
+ public static RouteHandlerBuilder ExcludeFromMultiTenantResolution(this RouteHandlerBuilder builder)
+ => ExcludeFromMultiTenantResolution(builder);
+}
\ No newline at end of file
diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Extensions/MultiTenantBuilderExtensions.cs b/src/Finbuckle.MultiTenant.AspNetCore/Extensions/MultiTenantBuilderExtensions.cs
index 1d9a6c6c..886e0840 100644
--- a/src/Finbuckle.MultiTenant.AspNetCore/Extensions/MultiTenantBuilderExtensions.cs
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Extensions/MultiTenantBuilderExtensions.cs
@@ -25,6 +25,39 @@ namespace Finbuckle.MultiTenant;
///
public static class MultiTenantBuilderExtensions
{
+ ///
+ /// Configures a callback that determines when endpoints should be short circuited
+ /// during MultiTenant resolution.
+ ///
+ /// The ITenantInfo implementation type.
+ /// The instance.
+ /// The short circuit options to configure.
+ /// The so that additional calls can be chained.
+ public static MultiTenantBuilder ShortCircuitWhen(
+ this MultiTenantBuilder builder, Action options)
+ where TTenantInfo : class, ITenantInfo, new()
+ {
+ builder.Services.Configure(options);
+
+ return builder;
+ }
+
+ ///
+ /// Configures endpoints to be short circuited during MultiTenant resolution when
+ /// no Tenant was resolved.
+ ///
+ /// The ITenantInfo implementation type.
+ /// The instance.
+ /// The so that additional calls can be chained.
+ public static MultiTenantBuilder ShortCircuitWhenTenantNotResolved(
+ this MultiTenantBuilder builder)
+ where TTenantInfo : class, ITenantInfo, new()
+ {
+ builder.Services.Configure(config => config.Predicate = context => !context.IsResolved);
+
+ return builder;
+ }
+
///
/// Configures authentication options to enable per-tenant behavior.
///
diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Internal/MultiTenantMiddleware.cs b/src/Finbuckle.MultiTenant.AspNetCore/Internal/MultiTenantMiddleware.cs
index 23ffcf22..17dfcec0 100644
--- a/src/Finbuckle.MultiTenant.AspNetCore/Internal/MultiTenantMiddleware.cs
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Internal/MultiTenantMiddleware.cs
@@ -3,8 +3,11 @@
using System.Threading.Tasks;
using Finbuckle.MultiTenant.Abstractions;
+using Finbuckle.MultiTenant.AspNetCore.Options;
+using Finbuckle.MultiTenant.AspNetCore.Routing;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
namespace Finbuckle.MultiTenant.AspNetCore.Internal;
@@ -14,14 +17,27 @@ namespace Finbuckle.MultiTenant.AspNetCore.Internal;
public class MultiTenantMiddleware
{
private readonly RequestDelegate next;
+ private readonly ShortCircuitWhenOptions? options;
public MultiTenantMiddleware(RequestDelegate next)
{
this.next = next;
}
+ public MultiTenantMiddleware(RequestDelegate next, IOptions options)
+ {
+ this.next = next;
+ this.options = options.Value;
+ }
+
public async Task Invoke(HttpContext context)
{
+ if (context.GetEndpoint()?.Metadata.GetMetadata() is { ExcludeFromResolution: true })
+ {
+ await next(context);
+ return;
+ }
+
context.RequestServices.GetRequiredService();
var mtcSetter = context.RequestServices.GetRequiredService();
@@ -30,7 +46,8 @@ public async Task Invoke(HttpContext context)
var multiTenantContext = await resolver.ResolveAsync(context);
mtcSetter.MultiTenantContext = multiTenantContext;
context.Items[typeof(IMultiTenantContext)] = multiTenantContext;
-
- await next(context);
+
+ if (options?.Predicate is null || !options.Predicate(multiTenantContext))
+ await next(context);
}
}
\ No newline at end of file
diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Options/ShortCircuitWhenOptions.cs b/src/Finbuckle.MultiTenant.AspNetCore/Options/ShortCircuitWhenOptions.cs
new file mode 100644
index 00000000..bf3050fd
--- /dev/null
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Options/ShortCircuitWhenOptions.cs
@@ -0,0 +1,25 @@
+using Finbuckle.MultiTenant.Abstractions;
+
+namespace Finbuckle.MultiTenant.AspNetCore.Options;
+
+public class ShortCircuitWhenOptions
+{
+ private Func? _predicate;
+
+ ///
+ /// The callback that determines if the endpoint should be short circuited.
+ ///
+ public Func? Predicate
+ {
+ get
+ {
+ return _predicate;
+ }
+ set
+ {
+ ArgumentNullException.ThrowIfNull(value);
+
+ _predicate = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Routing/ExcludeFromMultiTenantResolutionAttribute.cs b/src/Finbuckle.MultiTenant.AspNetCore/Routing/ExcludeFromMultiTenantResolutionAttribute.cs
new file mode 100644
index 00000000..f8202b0f
--- /dev/null
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Routing/ExcludeFromMultiTenantResolutionAttribute.cs
@@ -0,0 +1,13 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Finbuckle.MultiTenant.AspNetCore.Routing;
+
+///
+/// Indicates that this should be excluded from MultiTenant resolution.
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = false, Inherited = true)]
+public class ExcludeFromMultiTenantResolutionAttribute : Attribute, IExcludeFromMultiTenantResolutionMetadata
+{
+ ///
+ public bool ExcludeFromResolution => true;
+}
\ No newline at end of file
diff --git a/src/Finbuckle.MultiTenant.AspNetCore/Routing/IExcludeFromMultiTenantResolutionMetadata.cs b/src/Finbuckle.MultiTenant.AspNetCore/Routing/IExcludeFromMultiTenantResolutionMetadata.cs
new file mode 100644
index 00000000..6a519e10
--- /dev/null
+++ b/src/Finbuckle.MultiTenant.AspNetCore/Routing/IExcludeFromMultiTenantResolutionMetadata.cs
@@ -0,0 +1,16 @@
+using Microsoft.AspNetCore.Http;
+
+namespace Finbuckle.MultiTenant.AspNetCore.Routing;
+
+///
+/// Indicates whether MultiTenant resolution should occur for this .
+///
+public interface IExcludeFromMultiTenantResolutionMetadata
+{
+ ///
+ /// Gets a value indicating whether MultiTenant resolution should
+ /// occur for this . If ,
+ /// tenant resolution will not be executed.
+ ///
+ bool ExcludeFromResolution { get; }
+}
\ No newline at end of file
diff --git a/test/Finbuckle.MultiTenant.AspNetCore.Test/MultiTenantMiddlewareShould.cs b/test/Finbuckle.MultiTenant.AspNetCore.Test/MultiTenantMiddlewareShould.cs
index 86d61ac0..847de112 100644
--- a/test/Finbuckle.MultiTenant.AspNetCore.Test/MultiTenantMiddlewareShould.cs
+++ b/test/Finbuckle.MultiTenant.AspNetCore.Test/MultiTenantMiddlewareShould.cs
@@ -5,8 +5,11 @@
using System.Threading.Tasks;
using Finbuckle.MultiTenant.Abstractions;
using Finbuckle.MultiTenant.AspNetCore.Internal;
+using Finbuckle.MultiTenant.AspNetCore.Options;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using Moq;
using Xunit;
@@ -27,6 +30,7 @@ public async void SetHttpContextItemIfTenantFound()
var context = new Mock();
context.Setup(c => c.RequestServices).Returns(sp);
+ context.Setup(c => c.Features).Returns(new FeatureCollection());
var itemsDict = new Dictionary