UseMiddleware resolves InvokeAsync in addition to Invoke

This commit is contained in:
John Luo 2017-03-25 23:42:14 -07:00
parent b3b846c27e
commit 2bdbbbf41d
5 changed files with 177 additions and 100 deletions

View File

@ -17,7 +17,8 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
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<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodinfo, ParameterInfo[] parameters)
{
// If we call something like
//
//
// public class Middleware
// {
// public Task Invoke(HttpContext context, ILoggerFactory loggeryFactory)
// {
//
//
// }
// }
//

View File

@ -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
/// <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);
Task InvokeAsync(HttpContext context, RequestDelegate next);
}
}

View File

@ -37,9 +37,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions
/// <summary>
/// No public '{0}' method found.
/// </summary>
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);
}
/// <summary>
@ -53,9 +53,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions
/// <summary>
/// '{0}' does not return an object of type '{1}'.
/// </summary>
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);
}
/// <summary>
@ -69,9 +69,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions
/// <summary>
/// The '{0}' method's first argument must be of type '{1}'.
/// </summary>
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);
}
/// <summary>
@ -85,9 +85,9 @@ namespace Microsoft.AspNetCore.Http.Abstractions
/// <summary>
/// Multiple public '{0}' methods are available.
/// </summary>
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);
}
/// <summary>

View File

@ -121,16 +121,16 @@
<value>'{0}' is not available.</value>
</data>
<data name="Exception_UseMiddlewareNoInvokeMethod" xml:space="preserve">
<value>No public '{0}' method found.</value>
<value>No public '{0}' or '{1}' method found.</value>
</data>
<data name="Exception_UseMiddlewareNonTaskReturnType" xml:space="preserve">
<value>'{0}' does not return an object of type '{1}'.</value>
<value>'{0}' or '{1}' does not return an object of type '{2}'.</value>
</data>
<data name="Exception_UseMiddlewareNoParameters" xml:space="preserve">
<value>The '{0}' method's first argument must be of type '{1}'.</value>
<value>The '{0}' or '{1}' method's first argument must be of type '{2}'.</value>
</data>
<data name="Exception_UseMiddleMutlipleInvokes" xml:space="preserve">
<value>Multiple public '{0}' methods are available.</value>
<value>Multiple public '{0}' or '{1}' methods are available.</value>
</data>
<data name="Exception_PathMustStartWithSlash" xml:space="preserve">
<value>The path in '{0}' must start with '/'.</value>

View File

@ -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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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<InvalidOperationException>(() => 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;
}
}
}