Hoist method attributes from the request delegate as metadata. (#6911)
- This should allow a more declarative approach to declaring endpoint metadata using the default methods. - Attributes are applied first and can be overridden imperatively
This commit is contained in:
parent
14d8e33a93
commit
9f71e60283
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
|
|
@ -21,7 +22,6 @@ namespace Microsoft.AspNetCore.Builder
|
|||
private static readonly string[] PutVerb = new[] { "PUT" };
|
||||
private static readonly string[] DeleteVerb = new[] { "DELETE" };
|
||||
|
||||
#region MapVerbs
|
||||
/// <summary>
|
||||
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP GET requests
|
||||
/// for the specified pattern.
|
||||
|
|
@ -227,9 +227,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
|
||||
return Map(builder, pattern, displayName ?? $"{pattern} HTTP: {string.Join(", ", httpMethods)}", requestDelegate, metadata: resolvedMetadata.ToArray());
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Map
|
||||
/// <summary>
|
||||
/// Adds a <see cref="RouteEndpoint"/> to the <see cref="IEndpointRouteBuilder"/> that matches HTTP requests
|
||||
/// for the specified pattern.
|
||||
|
|
@ -323,8 +321,17 @@ namespace Microsoft.AspNetCore.Builder
|
|||
var routeEndpointBuilder = new RouteEndpointBuilder(
|
||||
requestDelegate,
|
||||
pattern,
|
||||
defaultOrder);
|
||||
routeEndpointBuilder.DisplayName = displayName;
|
||||
defaultOrder)
|
||||
{
|
||||
DisplayName = displayName
|
||||
};
|
||||
|
||||
// Add delegate attributes as metadata
|
||||
foreach (var attribute in requestDelegate.Method.GetCustomAttributes())
|
||||
{
|
||||
routeEndpointBuilder.Metadata.Add(attribute);
|
||||
}
|
||||
|
||||
if (metadata != null)
|
||||
{
|
||||
foreach (var item in metadata)
|
||||
|
|
@ -343,6 +350,5 @@ namespace Microsoft.AspNetCore.Builder
|
|||
|
||||
return modelEndpointDataSource.AddEndpointBuilder(routeEndpointBuilder);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@
|
|||
// 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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Routing.Matching;
|
||||
using Microsoft.AspNetCore.Routing.Patterns;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -97,5 +95,59 @@ namespace Microsoft.AspNetCore.Builder
|
|||
Assert.Equal("/", endpointBuilder1.RoutePattern.RawText);
|
||||
Assert.Equal(metadata, Assert.Single(endpointBuilder1.Metadata));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapEndpoint_AttributesCollectedAsMetadata()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultEndpointRouteBuilder();
|
||||
|
||||
// Act
|
||||
var endpointBuilder = builder.Map(RoutePatternFactory.Parse("/"), "Display name!", Handle);
|
||||
|
||||
// Assert
|
||||
var endpointBuilder1 = GetRouteEndpointBuilder(builder);
|
||||
Assert.Equal("Display name!", endpointBuilder1.DisplayName);
|
||||
Assert.Equal("/", endpointBuilder1.RoutePattern.RawText);
|
||||
Assert.Equal(2, endpointBuilder1.Metadata.Count);
|
||||
Assert.IsType<Attribute1>(endpointBuilder1.Metadata[0]);
|
||||
Assert.IsType<Attribute2>(endpointBuilder1.Metadata[1]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapEndpoint_ExplicitMetadataAddedAfterAttributeMetadata()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new DefaultEndpointRouteBuilder();
|
||||
|
||||
// Act
|
||||
var endpointBuilder = builder.Map(RoutePatternFactory.Parse("/"), "Display name!", Handle, new Metadata());
|
||||
|
||||
// Assert
|
||||
var endpointBuilder1 = GetRouteEndpointBuilder(builder);
|
||||
Assert.Equal("Display name!", endpointBuilder1.DisplayName);
|
||||
Assert.Equal("/", endpointBuilder1.RoutePattern.RawText);
|
||||
Assert.Equal(3, endpointBuilder1.Metadata.Count);
|
||||
Assert.IsType<Attribute1>(endpointBuilder1.Metadata[0]);
|
||||
Assert.IsType<Attribute2>(endpointBuilder1.Metadata[1]);
|
||||
Assert.IsType<Metadata>(endpointBuilder1.Metadata[2]);
|
||||
}
|
||||
|
||||
[Attribute1]
|
||||
[Attribute2]
|
||||
private static Task Handle(HttpContext context) => Task.CompletedTask;
|
||||
|
||||
private class Attribute1 : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
private class Attribute2 : Attribute
|
||||
{
|
||||
}
|
||||
|
||||
private class Metadata
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
|
@ -46,6 +47,10 @@ namespace RoutingSandbox
|
|||
foreach (var endpoint in dataSource.Endpoints.OfType<RouteEndpoint>().OrderBy(e => e.RoutePattern.RawText, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
sb.AppendLine($"- {endpoint.RoutePattern.RawText}");
|
||||
foreach (var metadata in endpoint.Metadata)
|
||||
{
|
||||
sb.AppendLine(" " + metadata);
|
||||
}
|
||||
}
|
||||
|
||||
var response = httpContext.Response;
|
||||
|
|
@ -79,6 +84,10 @@ namespace RoutingSandbox
|
|||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
builder.MapGet("/attributes", HandlerWithAttributes);
|
||||
|
||||
builder.Map("/getwithattributes", Handler);
|
||||
|
||||
builder.MapFramework(frameworkBuilder =>
|
||||
{
|
||||
frameworkBuilder.AddPattern("/transform/{hub:slugify=TestHub}/{method:slugify=TestMethod}");
|
||||
|
|
@ -92,5 +101,29 @@ namespace RoutingSandbox
|
|||
|
||||
app.UseStaticFiles();
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
private Task HandlerWithAttributes(HttpContext context)
|
||||
{
|
||||
return context.Response.WriteAsync("I have ann authorize attribute");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
private Task Handler(HttpContext context)
|
||||
{
|
||||
return context.Response.WriteAsync("I have a method metadata attribute");
|
||||
}
|
||||
|
||||
private class AuthorizeAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private class HttpGetAttribute : Attribute, IHttpMethodMetadata
|
||||
{
|
||||
public bool AcceptCorsPreflight => false;
|
||||
|
||||
public IReadOnlyList<string> HttpMethods { get; } = new List<string> { "GET" };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue