From 2bdbbbf41daa814977acbdc3bc5526c894120dc4 Mon Sep 17 00:00:00 2001 From: John Luo Date: Sat, 25 Mar 2017 23:42:14 -0700 Subject: [PATCH] UseMiddleware resolves InvokeAsync in addition to Invoke --- .../Extensions/UseMiddlewareExtensions.cs | 25 +- .../IMiddleware.cs | 6 +- .../Properties/Resources.Designer.cs | 16 +- .../Resources.resx | 8 +- .../UseMiddlewareTest.cs | 222 ++++++++++++------ 5 files changed, 177 insertions(+), 100 deletions(-) diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs index 71677c0582..88a79d7daa 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Extensions/UseMiddlewareExtensions.cs @@ -17,7 +17,8 @@ namespace Microsoft.AspNetCore.Builder /// public static class UseMiddlewareExtensions { - private const string InvokeMethodName = "Invoke"; + internal const string InvokeMethodName = "Invoke"; + internal const string InvokeAsyncMethodName = "InvokeAsync"; private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static); @@ -44,7 +45,7 @@ namespace Microsoft.AspNetCore.Builder { if (typeof(IMiddleware).GetTypeInfo().IsAssignableFrom(middleware.GetTypeInfo())) { - // IMiddleware doesn't support passing args directly since it's + // IMiddleware doesn't support passing args directly since it's // activated from the container if (args.Length > 0) { @@ -58,27 +59,31 @@ namespace Microsoft.AspNetCore.Builder return app.Use(next => { var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public); - var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)).ToArray(); + var invokeMethods = methods.Where(m => + string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal) + || string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal) + ).ToArray(); + if (invokeMethods.Length > 1) { - throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName)); + throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName)); } if (invokeMethods.Length == 0) { - throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName)); + throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName)); } var methodinfo = invokeMethods[0]; if (!typeof(Task).IsAssignableFrom(methodinfo.ReturnType)) { - throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, nameof(Task))); + throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task))); } var parameters = methodinfo.GetParameters(); if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext)) { - throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, nameof(HttpContext))); + throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext))); } var ctorArgs = new object[args.Length + 1]; @@ -127,7 +132,7 @@ namespace Microsoft.AspNetCore.Builder try { - await middleware.Invoke(context, next); + await middleware.InvokeAsync(context, next); } finally { @@ -140,12 +145,12 @@ namespace Microsoft.AspNetCore.Builder private static Func Compile(MethodInfo methodinfo, ParameterInfo[] parameters) { // If we call something like - // + // // public class Middleware // { // public Task Invoke(HttpContext context, ILoggerFactory loggeryFactory) // { - // + // // } // } // diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs index 53d549833b..f92527f3f5 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/IMiddleware.cs @@ -1,10 +1,6 @@ // 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 @@ -20,6 +16,6 @@ namespace Microsoft.AspNetCore.Http /// 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); + Task InvokeAsync(HttpContext context, RequestDelegate next); } } diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs index c91b54e3aa..3b747cd441 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Properties/Resources.Designer.cs @@ -37,9 +37,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions /// /// No public '{0}' method found. /// - internal static string FormatException_UseMiddlewareNoInvokeMethod(object p0) + internal static string FormatException_UseMiddlewareNoInvokeMethod(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNoInvokeMethod"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNoInvokeMethod"), p0, p1); } /// @@ -53,9 +53,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions /// /// '{0}' does not return an object of type '{1}'. /// - internal static string FormatException_UseMiddlewareNonTaskReturnType(object p0, object p1) + internal static string FormatException_UseMiddlewareNonTaskReturnType(object p0, object p1, object p2) { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNonTaskReturnType"), p0, p1); + return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNonTaskReturnType"), p0, p1, p2); } /// @@ -69,9 +69,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions /// /// The '{0}' method's first argument must be of type '{1}'. /// - internal static string FormatException_UseMiddlewareNoParameters(object p0, object p1) + internal static string FormatException_UseMiddlewareNoParameters(object p0, object p1, object p2) { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNoParameters"), p0, p1); + return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddlewareNoParameters"), p0, p1, p2); } /// @@ -85,9 +85,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions /// /// Multiple public '{0}' methods are available. /// - internal static string FormatException_UseMiddleMutlipleInvokes(object p0) + internal static string FormatException_UseMiddleMutlipleInvokes(object p0, object p1) { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddleMutlipleInvokes"), p0); + return string.Format(CultureInfo.CurrentCulture, GetString("Exception_UseMiddleMutlipleInvokes"), p0, p1); } /// diff --git a/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx b/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx index b37e46a114..969b4044a6 100644 --- a/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx +++ b/src/Microsoft.AspNetCore.Http.Abstractions/Resources.resx @@ -121,16 +121,16 @@ '{0}' is not available. - No public '{0}' method found. + No public '{0}' or '{1}' method found. - '{0}' does not return an object of type '{1}'. + '{0}' or '{1}' does not return an object of type '{2}'. - The '{0}' method's first argument must be of type '{1}'. + The '{0}' or '{1}' method's first argument must be of type '{2}'. - Multiple public '{0}' methods are available. + Multiple public '{0}' or '{1}' methods are available. The path in '{0}' must start with '/'. diff --git a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs index 8f0446ce35..6ba21da298 100644 --- a/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs +++ b/test/Microsoft.AspNetCore.Http.Abstractions.Tests/UseMiddlewareTest.cs @@ -7,6 +7,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder.Internal; using Microsoft.AspNetCore.Http.Abstractions; +using Microsoft.Extensions.Internal; using Xunit; namespace Microsoft.AspNetCore.Http @@ -16,60 +17,137 @@ namespace Microsoft.AspNetCore.Http [Fact] public void UseMiddleware_WithNoParameters_ThrowsException() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareNoParametersStub)); var exception = Assert.Throws(() => builder.Build()); - Assert.Equal(Resources.FormatException_UseMiddlewareNoParameters("Invoke", nameof(HttpContext)), exception.Message); + Assert.Equal( + Resources.FormatException_UseMiddlewareNoParameters( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName, + nameof(HttpContext)), + exception.Message); + } + + [Fact] + public void UseMiddleware_AsyncWithNoParameters_ThrowsException() + { + var builder = new ApplicationBuilder(new DummyServiceProvider()); + builder.UseMiddleware(typeof(MiddlewareAsyncNoParametersStub)); + var exception = Assert.Throws(() => builder.Build()); + + Assert.Equal( + Resources.FormatException_UseMiddlewareNoParameters( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName, + nameof(HttpContext)), + exception.Message); } [Fact] public void UseMiddleware_NonTaskReturnType_ThrowsException() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareNonTaskReturnStub)); var exception = Assert.Throws(() => builder.Build()); - Assert.Equal(Resources.FormatException_UseMiddlewareNonTaskReturnType("Invoke", nameof(Task)), exception.Message); + + Assert.Equal( + Resources.FormatException_UseMiddlewareNonTaskReturnType( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName, + nameof(Task)), + exception.Message); } [Fact] - public void UseMiddleware_NoInvokeMethod_ThrowsException() + public void UseMiddleware_AsyncNonTaskReturnType_ThrowsException() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); + builder.UseMiddleware(typeof(MiddlewareAsyncNonTaskReturnStub)); + var exception = Assert.Throws(() => builder.Build()); + + Assert.Equal( + Resources.FormatException_UseMiddlewareNonTaskReturnType( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName, + nameof(Task)), + exception.Message); + } + + [Fact] + public void UseMiddleware_NoInvokeOrInvokeAsyncMethod_ThrowsException() + { + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareNoInvokeStub)); var exception = Assert.Throws(() => builder.Build()); - Assert.Equal(Resources.FormatException_UseMiddlewareNoInvokeMethod("Invoke"), exception.Message); + + Assert.Equal( + Resources.FormatException_UseMiddlewareNoInvokeMethod( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName), + exception.Message); } [Fact] public void UseMiddleware_MutlipleInvokeMethods_ThrowsException() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareMultipleInvokesStub)); var exception = Assert.Throws(() => builder.Build()); - Assert.Equal(Resources.FormatException_UseMiddleMutlipleInvokes("Invoke"), exception.Message); + + Assert.Equal( + Resources.FormatException_UseMiddleMutlipleInvokes( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName), + exception.Message); + } + + [Fact] + public void UseMiddleware_MutlipleInvokeAsyncMethods_ThrowsException() + { + var builder = new ApplicationBuilder(new DummyServiceProvider()); + builder.UseMiddleware(typeof(MiddlewareMultipleInvokeAsyncStub)); + var exception = Assert.Throws(() => builder.Build()); + + Assert.Equal( + Resources.FormatException_UseMiddleMutlipleInvokes( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName), + exception.Message); + } + + [Fact] + public void UseMiddleware_MutlipleInvokeAndInvokeAsyncMethods_ThrowsException() + { + var builder = new ApplicationBuilder(new DummyServiceProvider()); + builder.UseMiddleware(typeof(MiddlewareMultipleInvokeAndInvokeAsyncStub)); + var exception = Assert.Throws(() => builder.Build()); + + Assert.Equal( + Resources.FormatException_UseMiddleMutlipleInvokes( + UseMiddlewareExtensions.InvokeMethodName, + UseMiddlewareExtensions.InvokeAsyncMethodName), + exception.Message); } [Fact] public async Task UseMiddleware_ThrowsIfArgCantBeResolvedFromContainer() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareInjectInvokeNoService)); var app = builder.Build(); var exception = await Assert.ThrowsAsync(() => app(new DefaultHttpContext())); - Assert.Equal(Resources.FormatException_InvokeMiddlewareNoService(typeof(object), typeof(MiddlewareInjectInvokeNoService)), exception.Message); + Assert.Equal( + Resources.FormatException_InvokeMiddlewareNoService( + typeof(object), + typeof(MiddlewareInjectInvokeNoService)), + exception.Message); } [Fact] public void UseMiddlewareWithInvokeArg() { - var mockServiceProvider = new DummyServiceProvider(); - var builder = new ApplicationBuilder(mockServiceProvider); + var builder = new ApplicationBuilder(new DummyServiceProvider()); builder.UseMiddleware(typeof(MiddlewareInjectInvoke)); var app = builder.Build(); app(new DefaultHttpContext()); @@ -126,7 +204,11 @@ namespace Microsoft.AspNetCore.Http await app(context); }); - Assert.Equal(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(typeof(BadMiddlewareFactory), typeof(Middleware)), exception.Message); + Assert.Equal( + Resources.FormatException_UseMiddlewareUnableToCreateMiddleware( + typeof(BadMiddlewareFactory), + typeof(Middleware)), + exception.Message); } [Fact] @@ -153,7 +235,7 @@ namespace Microsoft.AspNetCore.Http public class Middleware : IMiddleware { - public async Task Invoke(HttpContext context, RequestDelegate next) + public async Task InvokeAsync(HttpContext context, RequestDelegate next) { context.Items["before"] = true; await next(context); @@ -180,15 +262,9 @@ namespace Microsoft.AspNetCore.Http public class BadMiddlewareFactory : IMiddlewareFactory { - public IMiddleware Create(Type middlewareType) - { - return null; - } + public IMiddleware Create(Type middlewareType) => null; - public void Release(IMiddleware middleware) - { - - } + public void Release(IMiddleware middleware) { } } private class DummyServiceProvider : IServiceProvider @@ -214,9 +290,7 @@ namespace Microsoft.AspNetCore.Http public class MiddlewareInjectWithOutAndRefParams { - public MiddlewareInjectWithOutAndRefParams(RequestDelegate next) - { - } + public MiddlewareInjectWithOutAndRefParams(RequestDelegate next) { } public Task Invoke(HttpContext context, ref IServiceProvider sp1, out IServiceProvider sp2) { @@ -228,74 +302,76 @@ namespace Microsoft.AspNetCore.Http private class MiddlewareInjectInvokeNoService { - public MiddlewareInjectInvokeNoService(RequestDelegate next) - { - } + public MiddlewareInjectInvokeNoService(RequestDelegate next) { } - public Task Invoke(HttpContext context, object value) - { - return Task.FromResult(0); - } + public Task Invoke(HttpContext context, object value) => TaskCache.CompletedTask; } private class MiddlewareInjectInvoke { - public MiddlewareInjectInvoke(RequestDelegate next) - { - } + public MiddlewareInjectInvoke(RequestDelegate next) { } - public Task Invoke(HttpContext context, IServiceProvider provider) - { - return Task.FromResult(0); - } + public Task Invoke(HttpContext context, IServiceProvider provider) => TaskCache.CompletedTask; } private class MiddlewareNoParametersStub { - public MiddlewareNoParametersStub(RequestDelegate next) - { - } + public MiddlewareNoParametersStub(RequestDelegate next) { } - public Task Invoke() - { - return Task.FromResult(0); - } + public Task Invoke() => TaskCache.CompletedTask; + } + + private class MiddlewareAsyncNoParametersStub + { + public MiddlewareAsyncNoParametersStub(RequestDelegate next) { } + + public Task InvokeAsync() => TaskCache.CompletedTask; } private class MiddlewareNonTaskReturnStub { - public MiddlewareNonTaskReturnStub(RequestDelegate next) - { - } + public MiddlewareNonTaskReturnStub(RequestDelegate next) { } - public int Invoke() - { - return 0; - } + public int Invoke() => 0; + } + + private class MiddlewareAsyncNonTaskReturnStub + { + public MiddlewareAsyncNonTaskReturnStub(RequestDelegate next) { } + + public int InvokeAsync() => 0; } private class MiddlewareNoInvokeStub { - public MiddlewareNoInvokeStub(RequestDelegate next) - { - } + public MiddlewareNoInvokeStub(RequestDelegate next) { } } private class MiddlewareMultipleInvokesStub { - public MiddlewareMultipleInvokesStub(RequestDelegate next) - { - } + public MiddlewareMultipleInvokesStub(RequestDelegate next) { } - public Task Invoke(HttpContext context) - { - return Task.FromResult(0); - } + public Task Invoke(HttpContext context) => TaskCache.CompletedTask; - public Task Invoke(HttpContext context, int i) - { - return Task.FromResult(0); - } + public Task Invoke(HttpContext context, int i) => TaskCache.CompletedTask; + } + + private class MiddlewareMultipleInvokeAsyncStub + { + public MiddlewareMultipleInvokeAsyncStub(RequestDelegate next) { } + + public Task InvokeAsync(HttpContext context) => TaskCache.CompletedTask; + + public Task InvokeAsync(HttpContext context, int i) => TaskCache.CompletedTask; + } + + private class MiddlewareMultipleInvokeAndInvokeAsyncStub + { + public MiddlewareMultipleInvokeAndInvokeAsyncStub(RequestDelegate next) { } + + public Task Invoke(HttpContext context) => TaskCache.CompletedTask; + + public Task InvokeAsync(HttpContext context) => TaskCache.CompletedTask; } } } \ No newline at end of file