-
Notifications
You must be signed in to change notification settings - Fork 191
Added support for middleware activation via IMiddlewareFactory #773
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,6 +42,18 @@ public static IApplicationBuilder UseMiddleware<TMiddleware>(this IApplicationBu | |
/// <returns>The <see cref="IApplicationBuilder"/> instance.</returns> | ||
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 @@ public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Ty | |
}); | ||
} | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's kind of interesting how the type winds up just being used as a key and nothing else. |
||
if (middleware == null) | ||
{ | ||
// The factory returned null, it's a broken implementation | ||
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareType)); | ||
} | ||
|
||
try | ||
{ | ||
await middleware.Invoke(context, next); | ||
} | ||
finally | ||
{ | ||
middlewareFactory.Release(middleware); | ||
} | ||
}; | ||
}); | ||
} | ||
|
||
private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters) | ||
{ | ||
// If we call something like | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <summary> | ||
/// Defines middleware that can be added to the application's request pipeline. | ||
/// </summary> | ||
public interface IMiddleware | ||
{ | ||
/// <summary> | ||
/// Request handling method. | ||
/// </summary> | ||
/// <param name="context">The <see cref="HttpContext"/> for the current request.</param> | ||
/// <param name="next">The delegate representing the remaining middleware in the request pipeline.</param> | ||
/// <returns>A <see cref="Task"/> that represents the execution of this middleware.</returns> | ||
Task Invoke(HttpContext context, RequestDelegate next); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
{ | ||
/// <summary> | ||
/// Provides methods to create middlware. | ||
/// </summary> | ||
public interface IMiddlewareFactory | ||
{ | ||
/// <summary> | ||
/// Creates a middleware instance for each request. | ||
/// </summary> | ||
/// <param name="middlewareType">The concrete <see cref="Type"/> of the <see cref="IMiddleware"/>.</param> | ||
/// <returns>The <see cref="IMiddleware"/> instance.</returns> | ||
IMiddleware Create(Type middlewareType); | ||
|
||
/// <summary> | ||
/// Releases a <see cref="IMiddleware"/> instance at the end of each request. | ||
/// </summary> | ||
/// <param name="middleware">The <see cref="IMiddleware"/> instance to release.</param> | ||
void Release(IMiddleware middleware); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -144,4 +144,13 @@ | |
<data name="Exception_PortMustBeGreaterThanZero" xml:space="preserve"> | ||
<value>The value must be greater than zero.</value> | ||
</data> | ||
<data name="Exception_UseMiddlewareNoMiddlewareFactory" xml:space="preserve"> | ||
<value>No service for type '{0}' has been registered.</value> | ||
</data> | ||
<data name="Exception_UseMiddlewareUnableToCreateMiddleware" xml:space="preserve"> | ||
<value>Unable to create middleware '{0}'.</value> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be a little more clear about it being the IMiddlewareFactory that's not providing an IMiddleware implementation of type {0}. |
||
</data> | ||
<data name="Exception_UseMiddlewareExplicitArgumentsNotSupported" xml:space="preserve"> | ||
<value>Types that implement '{0}' do not support explicit arguments.</value> | ||
</data> | ||
</root> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a companion PR in hosting where this is registered? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not yet, I'll do that after we agree we like this. |
||
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 | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So you've decide you're OK with throwing? #754 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I put it in the PR for feedback. I considered adding another overload but it would be confusing naming.