diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs
index b9f1d36064..71677c0582 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs
@@ -42,6 +42,18 @@ namespace Microsoft.AspNetCore.Builder
/// The instance.
public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
{
+ if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo()))
+ {
+ // IMiddleware doesn't support passing args directly since it's
+ // activated from the container
+ if (args.Length > 0)
+ {
+ throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
+ }
+
+ return UseMiddlewareInterface(app, middleware);
+ }
+
var applicationServices = app.ApplicationServices;
return app.Use(next =>
{
@@ -93,6 +105,38 @@ namespace Microsoft.AspNetCore.Builder
});
}
+ private static IApplicationBuilder UseMiddlewareInterface(IApplicationBuilder app, Type middlewareType)
+ {
+ return app.Use(next =>
+ {
+ return async context =>
+ {
+ var middlewareFactory = (IMiddlewareFactory)context.RequestServices.GetService(typeof(IMiddlewareFactory));
+ if (middlewareFactory == null)
+ {
+ // No middleware factory
+ throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
+ }
+
+ var middleware = middlewareFactory.Create(middlewareType);
+ if (middleware == null)
+ {
+ // The factory returned null, it's a broken implementation
+ throw new InvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(), middlewareType));
+ }
+
+ try
+ {
+ await middleware.Invoke(context, next);
+ }
+ finally
+ {
+ middlewareFactory.Release(middleware);
+ }
+ };
+ });
+ }
+
private static Func Compile(MethodInfo methodinfo, ParameterInfo[] parameters)
{
// If we call something like
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs
new file mode 100644
index 0000000000..53d549833b
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs
@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Http
+{
+ ///
+ /// Defines middleware that can be added to the application's request pipeline.
+ ///
+ public interface IMiddleware
+ {
+ ///
+ /// Request handling method.
+ ///
+ /// The for the current request.
+ /// The delegate representing the remaining middleware in the request pipeline.
+ /// A that represents the execution of this middleware.
+ Task Invoke(HttpContext context, RequestDelegate next);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/IMiddlewareFactory.cs b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddlewareFactory.cs
new file mode 100644
index 0000000000..be5af344f2
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddlewareFactory.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.Http
+{
+ ///
+ /// Provides methods to create middlware.
+ ///
+ public interface IMiddlewareFactory
+ {
+ ///
+ /// Creates a middleware instance for each request.
+ ///
+ /// The concrete of the .
+ /// The instance.
+ IMiddleware Create(Type middlewareType);
+
+ ///
+ /// Releases a instance at the end of each request.
+ ///
+ /// The instance to release.
+ void Release(IMiddleware middleware);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs
index 40e4255667..c91b54e3aa 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs
@@ -154,6 +154,54 @@ namespace Microsoft.AspNetCore.Http.Abstractions
return GetString("Exception_PortMustBeGreaterThanZero");
}
+ ///
+ /// No service for type '{0}' has been registered.
+ ///
+ internal static string Exception_UseMiddlewareNoMiddlewareFactory
+ {
+ get { return GetString("Exception_UseMiddlewareNoMiddlewareFactory"); }
+ }
+
+ ///
+ /// No service for type '{0}' has been registered.
+ ///
+ internal static string FormatException_UseMiddlewareNoMiddlewareFactory(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNoMiddlewareFactory"), p0);
+ }
+
+ ///
+ /// '{0}' failed to create middleware of type '{1}'.
+ ///
+ internal static string Exception_UseMiddlewareUnableToCreateMiddleware
+ {
+ get { return GetString("Exception_UseMiddlewareUnableToCreateMiddleware"); }
+ }
+
+ ///
+ /// '{0}' failed to create middleware of type '{1}'.
+ ///
+ internal static string FormatException_UseMiddlewareUnableToCreateMiddleware(object p0, object p1)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareUnableToCreateMiddleware"), p0, p1);
+ }
+
+ ///
+ /// Types that implement '{0}' do not support explicit arguments.
+ ///
+ internal static string Exception_UseMiddlewareExplicitArgumentsNotSupported
+ {
+ get { return GetString("Exception_UseMiddlewareExplicitArgumentsNotSupported"); }
+ }
+
+ ///
+ /// Types that implement '{0}' do not support explicit arguments.
+ ///
+ internal static string FormatException_UseMiddlewareExplicitArgumentsNotSupported(object p0)
+ {
+ return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareExplicitArgumentsNotSupported"), p0);
+ }
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx b/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx
index 19ce173d19..b37e46a114 100644
--- a/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx
+++ b/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx
@@ -144,4 +144,13 @@
The value must be greater than zero.
+
+ No service for type '{0}' has been registered.
+
+
+ '{0}' failed to create middleware of type '{1}'.
+
+
+ Types that implement '{0}' do not support explicit arguments.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Http/MiddlewareFactory.cs b/src/Microsoft.AspNetCore.Http/MiddlewareFactory.cs
new file mode 100644
index 0000000000..5e5cd285f4
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Http/MiddlewareFactory.cs
@@ -0,0 +1,35 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.Http
+{
+ public class MiddlewareFactory : IMiddlewareFactory
+ {
+ // The default middleware factory is just an IServiceProvider proxy.
+ // This should be registered as a scoped service so that the middleware instances
+ // don't end up being singletons.
+ private readonly IServiceProvider _serviceProvider;
+
+ public MiddlewareFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ public IMiddleware Create(Type middlewareType)
+ {
+ return _serviceProvider.GetRequiredService(middlewareType) as IMiddleware;
+ }
+
+ public void Release(IMiddleware middleware)
+ {
+ // The container owns the lifetime of the service
+ }
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs
index ce197e0c17..8f0446ce35 100644
--- a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs
+++ b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs
@@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Builder.Internal;
@@ -83,14 +84,130 @@ namespace Microsoft.AspNetCore.Http
var exception = Assert.Throws(() => builder.Build());
}
+ [Fact]
+ public void UseMiddlewareWithIMiddlewareThrowsIfParametersSpecified()
+ {
+ var mockServiceProvider = new DummyServiceProvider();
+ var builder = new ApplicationBuilder(mockServiceProvider);
+ var exception = Assert.Throws(() => builder.UseMiddleware(typeof(Middleware), "arg"));
+ Assert.Equal(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)), exception.Message);
+ }
+
+ [Fact]
+ public async Task UseMiddlewareWithIMiddlewareThrowsIfNoIMiddlewareFactoryRegistered()
+ {
+ var mockServiceProvider = new DummyServiceProvider();
+ var builder = new ApplicationBuilder(mockServiceProvider);
+ builder.UseMiddleware(typeof(Middleware));
+ var app = builder.Build();
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ var context = new DefaultHttpContext();
+ var sp = new DummyServiceProvider();
+ context.RequestServices = sp;
+ await app(context);
+ });
+ Assert.Equal(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)), exception.Message);
+ }
+
+ [Fact]
+ public async Task UseMiddlewareWithIMiddlewareThrowsIfMiddlewareFactoryCreateReturnsNull()
+ {
+ var mockServiceProvider = new DummyServiceProvider();
+ var builder = new ApplicationBuilder(mockServiceProvider);
+ builder.UseMiddleware(typeof(Middleware));
+ var app = builder.Build();
+ var exception = await Assert.ThrowsAsync(async () =>
+ {
+ var context = new DefaultHttpContext();
+ var sp = new DummyServiceProvider();
+ sp.AddService(typeof(IMiddlewareFactory), new BadMiddlewareFactory());
+ context.RequestServices = sp;
+ await app(context);
+ });
+
+ Assert.Equal(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(typeof(BadMiddlewareFactory), typeof(Middleware)), exception.Message);
+ }
+
+ [Fact]
+ public async Task UseMiddlewareWithIMiddlewareWorks()
+ {
+ var mockServiceProvider = new DummyServiceProvider();
+ var builder = new ApplicationBuilder(mockServiceProvider);
+ builder.UseMiddleware(typeof(Middleware));
+ var app = builder.Build();
+ var context = new DefaultHttpContext();
+ var sp = new DummyServiceProvider();
+ var middlewareFactory = new BasicMiddlewareFactory();
+ sp.AddService(typeof(IMiddlewareFactory), middlewareFactory);
+ context.RequestServices = sp;
+ await app(context);
+ Assert.Equal(true, context.Items["before"]);
+ Assert.Equal(true, context.Items["after"]);
+ Assert.NotNull(middlewareFactory.Created);
+ Assert.NotNull(middlewareFactory.Released);
+ Assert.IsType(typeof(Middleware), middlewareFactory.Created);
+ Assert.IsType(typeof(Middleware), middlewareFactory.Released);
+ Assert.Same(middlewareFactory.Created, middlewareFactory.Released);
+ }
+
+ public class Middleware : IMiddleware
+ {
+ public async Task Invoke(HttpContext context, RequestDelegate next)
+ {
+ context.Items["before"] = true;
+ await next(context);
+ context.Items["after"] = true;
+ }
+ }
+
+ public class BasicMiddlewareFactory : IMiddlewareFactory
+ {
+ public IMiddleware Created { get; private set; }
+ public IMiddleware Released { get; private set; }
+
+ public IMiddleware Create(Type middlewareType)
+ {
+ Created = Activator.CreateInstance(middlewareType) as IMiddleware;
+ return Created;
+ }
+
+ public void Release(IMiddleware middleware)
+ {
+ Released = middleware;
+ }
+ }
+
+ public class BadMiddlewareFactory : IMiddlewareFactory
+ {
+ public IMiddleware Create(Type middlewareType)
+ {
+ return null;
+ }
+
+ public void Release(IMiddleware middleware)
+ {
+
+ }
+ }
+
private class DummyServiceProvider : IServiceProvider
{
+ private Dictionary _services = new Dictionary();
+
+ public void AddService(Type type, object value) => _services[type] = value;
+
public object GetService(Type serviceType)
{
if (serviceType == typeof(IServiceProvider))
{
return this;
}
+
+ if (_services.TryGetValue(serviceType, out object value))
+ {
+ return value;
+ }
return null;
}
}