Add EndpointBuilder (#701)
This commit is contained in:
parent
14a3a98f48
commit
95267a32e2
|
|
@ -2,7 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Internal;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Matching;
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class EndpointDataSourceBuilderExtensions
|
||||||
|
{
|
||||||
|
public static EndpointBuilder MapHello(this EndpointDataSourceBuilder builder, string template, string greeter)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
var pipeline = builder.CreateApplicationBuilder()
|
||||||
|
.UseHello(greeter)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return builder.MapEndpoint(
|
||||||
|
(next) => pipeline,
|
||||||
|
template,
|
||||||
|
"Hello");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.Extensions.Options;
|
||||||
|
using RoutingSample.Web.HelloExtension;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class HelloAppBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseHello(this IApplicationBuilder app, string greeter)
|
||||||
|
{
|
||||||
|
if (app == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(app));
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.UseMiddleware<HelloMiddleware>(Options.Create(new HelloOptions
|
||||||
|
{
|
||||||
|
Greeter = greeter
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
// 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.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace RoutingSample.Web.HelloExtension
|
||||||
|
{
|
||||||
|
public class HelloMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly HelloOptions _helloOptions;
|
||||||
|
private readonly byte[] _helloPayload;
|
||||||
|
|
||||||
|
public HelloMiddleware(RequestDelegate next, IOptions<HelloOptions> helloOptions)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_helloOptions = helloOptions.Value;
|
||||||
|
|
||||||
|
var payload = new List<byte>();
|
||||||
|
payload.AddRange(Encoding.UTF8.GetBytes("Hello"));
|
||||||
|
if (!string.IsNullOrEmpty(_helloOptions.Greeter))
|
||||||
|
{
|
||||||
|
payload.Add((byte)' ');
|
||||||
|
payload.AddRange(Encoding.UTF8.GetBytes(_helloOptions.Greeter));
|
||||||
|
}
|
||||||
|
_helloPayload = payload.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
var response = context.Response;
|
||||||
|
var payloadLength = _helloPayload.Length;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
response.ContentLength = payloadLength;
|
||||||
|
return response.Body.WriteAsync(_helloPayload, 0, payloadLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
namespace RoutingSample.Web.HelloExtension
|
||||||
|
{
|
||||||
|
public class HelloOptions
|
||||||
|
{
|
||||||
|
public string Greeter { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,20 +3,16 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Internal;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
using Microsoft.AspNetCore.Routing.Matching;
|
|
||||||
using Microsoft.AspNetCore.Routing.Patterns;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
||||||
|
|
||||||
namespace RoutingSample.Web
|
namespace RoutingSample.Web
|
||||||
{
|
{
|
||||||
public class UseEndpointRoutingStartup
|
public class UseEndpointRoutingStartup
|
||||||
{
|
{
|
||||||
private static readonly byte[] _homePayload = Encoding.UTF8.GetBytes("Endpoint Routing sample endpoints:" + Environment.NewLine + "/plaintext");
|
private static readonly byte[] _homePayload = Encoding.UTF8.GetBytes("Endpoint Routing sample endpoints:" + Environment.NewLine + "/plaintext");
|
||||||
private static readonly byte[] _helloWorldPayload = Encoding.UTF8.GetBytes("Hello, World!");
|
private static readonly byte[] _plainTextPayload = Encoding.UTF8.GetBytes("Plain text!");
|
||||||
|
|
||||||
public void ConfigureServices(IServiceCollection services)
|
public void ConfigureServices(IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
@ -26,65 +22,59 @@ namespace RoutingSample.Web
|
||||||
{
|
{
|
||||||
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
|
options.ConstraintMap.Add("endsWith", typeof(EndsWithStringMatchProcessor));
|
||||||
});
|
});
|
||||||
|
|
||||||
var endpointDataSource = new DefaultEndpointDataSource(new[]
|
|
||||||
{
|
|
||||||
new MatcherEndpoint((next) => (httpContext) =>
|
|
||||||
{
|
|
||||||
var response = httpContext.Response;
|
|
||||||
var payloadLength = _homePayload.Length;
|
|
||||||
response.StatusCode = 200;
|
|
||||||
response.ContentType = "text/plain";
|
|
||||||
response.ContentLength = payloadLength;
|
|
||||||
return response.Body.WriteAsync(_homePayload, 0, payloadLength);
|
|
||||||
},
|
|
||||||
RoutePatternFactory.Parse("/"),
|
|
||||||
0,
|
|
||||||
EndpointMetadataCollection.Empty,
|
|
||||||
"Home"),
|
|
||||||
new MatcherEndpoint((next) => (httpContext) =>
|
|
||||||
{
|
|
||||||
var response = httpContext.Response;
|
|
||||||
var payloadLength = _helloWorldPayload.Length;
|
|
||||||
response.StatusCode = 200;
|
|
||||||
response.ContentType = "text/plain";
|
|
||||||
response.ContentLength = payloadLength;
|
|
||||||
return response.Body.WriteAsync(_helloWorldPayload, 0, payloadLength);
|
|
||||||
},
|
|
||||||
RoutePatternFactory.Parse("/plaintext"),
|
|
||||||
0,
|
|
||||||
EndpointMetadataCollection.Empty,
|
|
||||||
"Plaintext"),
|
|
||||||
new MatcherEndpoint((next) => (httpContext) =>
|
|
||||||
{
|
|
||||||
var response = httpContext.Response;
|
|
||||||
response.StatusCode = 200;
|
|
||||||
response.ContentType = "text/plain";
|
|
||||||
return response.WriteAsync("WithConstraints");
|
|
||||||
},
|
|
||||||
RoutePatternFactory.Parse("/withconstraints/{id:endsWith(_001)}"),
|
|
||||||
0,
|
|
||||||
EndpointMetadataCollection.Empty,
|
|
||||||
"withconstraints"),
|
|
||||||
new MatcherEndpoint((next) => (httpContext) =>
|
|
||||||
{
|
|
||||||
var response = httpContext.Response;
|
|
||||||
response.StatusCode = 200;
|
|
||||||
response.ContentType = "text/plain";
|
|
||||||
return response.WriteAsync("withoptionalconstraints");
|
|
||||||
},
|
|
||||||
RoutePatternFactory.Parse("/withoptionalconstraints/{id:endsWith(_001)?}"),
|
|
||||||
0,
|
|
||||||
EndpointMetadataCollection.Empty,
|
|
||||||
"withoptionalconstraints"),
|
|
||||||
});
|
|
||||||
|
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<EndpointDataSource>(endpointDataSource));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app)
|
public void Configure(Microsoft.AspNetCore.Builder.IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseEndpointRouting();
|
app.UseEndpointRouting(builder =>
|
||||||
|
{
|
||||||
|
builder.MapHello("/helloworld", "World");
|
||||||
|
|
||||||
|
builder.MapEndpoint(
|
||||||
|
(next) => (httpContext) =>
|
||||||
|
{
|
||||||
|
var response = httpContext.Response;
|
||||||
|
var payloadLength = _homePayload.Length;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
response.ContentLength = payloadLength;
|
||||||
|
return response.Body.WriteAsync(_homePayload, 0, payloadLength);
|
||||||
|
},
|
||||||
|
"/",
|
||||||
|
"Home");
|
||||||
|
builder.MapEndpoint(
|
||||||
|
(next) => (httpContext) =>
|
||||||
|
{
|
||||||
|
var response = httpContext.Response;
|
||||||
|
var payloadLength = _plainTextPayload.Length;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
response.ContentLength = payloadLength;
|
||||||
|
return response.Body.WriteAsync(_plainTextPayload, 0, payloadLength);
|
||||||
|
},
|
||||||
|
"/plaintext",
|
||||||
|
"Plaintext");
|
||||||
|
builder.MapEndpoint(
|
||||||
|
(next) => (httpContext) =>
|
||||||
|
{
|
||||||
|
var response = httpContext.Response;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
return response.WriteAsync("WithConstraints");
|
||||||
|
},
|
||||||
|
"/withconstraints/{id:endsWith(_001)}",
|
||||||
|
"withconstraints");
|
||||||
|
builder.MapEndpoint(
|
||||||
|
(next) => (httpContext) =>
|
||||||
|
{
|
||||||
|
var response = httpContext.Response;
|
||||||
|
response.StatusCode = 200;
|
||||||
|
response.ContentType = "text/plain";
|
||||||
|
return response.WriteAsync("withoptionalconstraints");
|
||||||
|
},
|
||||||
|
"/withoptionalconstraints/{id:endsWith(_001)?}",
|
||||||
|
"withoptionalconstraints");
|
||||||
|
});
|
||||||
|
|
||||||
// Imagine some more stuff here...
|
// Imagine some more stuff here...
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,28 @@ using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
using Microsoft.AspNetCore.Routing.Internal;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Internal
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
public static class EndpointRoutingApplicationBuilderExtensions
|
public static class EndpointRoutingApplicationBuilderExtensions
|
||||||
{
|
{
|
||||||
private const string EndpointRoutingRegisteredKey = "__EndpointRoutingMiddlewareRegistered";
|
private const string EndpointRoutingRegisteredKey = "__EndpointRoutingMiddlewareRegistered";
|
||||||
|
|
||||||
public static IApplicationBuilder UseEndpointRouting(this IApplicationBuilder builder)
|
public static IApplicationBuilder UseEndpointRouting(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseEndpointRouting(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseEndpointRouting(this IApplicationBuilder builder, Action<EndpointDataSourceBuilder> configure)
|
||||||
{
|
{
|
||||||
VerifyRoutingIsRegistered(builder);
|
VerifyRoutingIsRegistered(builder);
|
||||||
|
|
||||||
|
if (configure != null)
|
||||||
|
{
|
||||||
|
var dataSourceBuilder = (DefaultEndpointDataSourceBuilder)builder.ApplicationServices.GetRequiredService<EndpointDataSourceBuilder>();
|
||||||
|
dataSourceBuilder.ApplicationBuilder = builder;
|
||||||
|
configure(dataSourceBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
builder.Properties[EndpointRoutingRegisteredKey] = true;
|
builder.Properties[EndpointRoutingRegisteredKey] = true;
|
||||||
|
|
||||||
return builder.UseMiddleware<EndpointRoutingMiddleware>();
|
return builder.UseMiddleware<EndpointRoutingMiddleware>();
|
||||||
|
|
@ -50,4 +62,4 @@ namespace Microsoft.AspNetCore.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public static class MapEndpointEndpointDataSourceBuilderExtensions
|
||||||
|
{
|
||||||
|
public static MatcherEndpointBuilder MapEndpoint(
|
||||||
|
this EndpointDataSourceBuilder builder,
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
|
string pattern,
|
||||||
|
string displayName)
|
||||||
|
{
|
||||||
|
return MapEndpoint(builder, invoker, pattern, displayName, metadata: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatcherEndpointBuilder MapEndpoint(
|
||||||
|
this EndpointDataSourceBuilder builder,
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
|
RoutePattern pattern,
|
||||||
|
string displayName)
|
||||||
|
{
|
||||||
|
return MapEndpoint(builder, invoker, pattern, displayName, metadata: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatcherEndpointBuilder MapEndpoint(
|
||||||
|
this EndpointDataSourceBuilder builder,
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
|
string pattern,
|
||||||
|
string displayName,
|
||||||
|
IList<object> metadata)
|
||||||
|
{
|
||||||
|
return MapEndpoint(builder, invoker, RoutePatternFactory.Parse(pattern), displayName, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MatcherEndpointBuilder MapEndpoint(
|
||||||
|
this EndpointDataSourceBuilder builder,
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
|
RoutePattern pattern,
|
||||||
|
string displayName,
|
||||||
|
IList<object> metadata)
|
||||||
|
{
|
||||||
|
const int defaultOrder = 0;
|
||||||
|
|
||||||
|
var endpointBuilder = new MatcherEndpointBuilder(
|
||||||
|
invoker,
|
||||||
|
pattern,
|
||||||
|
defaultOrder);
|
||||||
|
endpointBuilder.DisplayName = displayName;
|
||||||
|
if (metadata != null)
|
||||||
|
{
|
||||||
|
foreach (var item in metadata)
|
||||||
|
{
|
||||||
|
endpointBuilder.Metadata.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Endpoints.Add(endpointBuilder);
|
||||||
|
return endpointBuilder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// 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 Microsoft.Extensions.FileProviders;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing
|
||||||
|
{
|
||||||
|
internal class BuilderEndpointDataSource : EndpointDataSource
|
||||||
|
{
|
||||||
|
private readonly EndpointDataSourceBuilder _builder;
|
||||||
|
|
||||||
|
public BuilderEndpointDataSource(EndpointDataSourceBuilder builder)
|
||||||
|
{
|
||||||
|
if (builder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(builder));
|
||||||
|
}
|
||||||
|
|
||||||
|
_builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IChangeToken GetChangeToken()
|
||||||
|
{
|
||||||
|
return NullChangeToken.Singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyList<Endpoint> Endpoints => _builder.Endpoints.Select(b => b.Build()).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing
|
||||||
|
{
|
||||||
|
internal class DefaultEndpointDataSourceBuilder : EndpointDataSourceBuilder
|
||||||
|
{
|
||||||
|
public IApplicationBuilder ApplicationBuilder { get; set; }
|
||||||
|
|
||||||
|
public override ICollection<EndpointBuilder> Endpoints { get; } = new List<EndpointBuilder>();
|
||||||
|
|
||||||
|
public override IApplicationBuilder CreateApplicationBuilder() => ApplicationBuilder.New();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -59,6 +59,12 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
return new CompositeEndpointDataSource(options.Value.DataSources);
|
return new CompositeEndpointDataSource(options.Value.DataSources);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
// Endpoint Infrastructure
|
||||||
|
//
|
||||||
|
services.TryAddSingleton<EndpointDataSource, BuilderEndpointDataSource>();
|
||||||
|
services.TryAddSingleton<EndpointDataSourceBuilder, DefaultEndpointDataSourceBuilder>();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Default matcher implementation
|
// Default matcher implementation
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing
|
||||||
|
{
|
||||||
|
public abstract class EndpointBuilder
|
||||||
|
{
|
||||||
|
public string DisplayName { get; set; }
|
||||||
|
|
||||||
|
public IList<object> Metadata { get; } = new List<object>();
|
||||||
|
|
||||||
|
public abstract Endpoint Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing
|
||||||
|
{
|
||||||
|
public abstract class EndpointDataSourceBuilder
|
||||||
|
{
|
||||||
|
public abstract ICollection<EndpointBuilder> Endpoints { get; }
|
||||||
|
|
||||||
|
public abstract IApplicationBuilder CreateApplicationBuilder();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
|
{
|
||||||
|
public sealed class MatcherEndpointBuilder : EndpointBuilder
|
||||||
|
{
|
||||||
|
public Func<RequestDelegate, RequestDelegate> Invoker { get; set; }
|
||||||
|
|
||||||
|
public RoutePattern RoutePattern { get; set; }
|
||||||
|
|
||||||
|
public int Order { get; set; }
|
||||||
|
|
||||||
|
public MatcherEndpointBuilder(
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker,
|
||||||
|
RoutePattern routePattern,
|
||||||
|
int order)
|
||||||
|
{
|
||||||
|
Invoker = invoker;
|
||||||
|
RoutePattern = routePattern;
|
||||||
|
Order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Endpoint Build()
|
||||||
|
{
|
||||||
|
var matcherEndpoint = new MatcherEndpoint(
|
||||||
|
Invoker,
|
||||||
|
RoutePattern,
|
||||||
|
Order,
|
||||||
|
new EndpointMetadataCollection(Metadata),
|
||||||
|
DisplayName);
|
||||||
|
|
||||||
|
return matcherEndpoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Routing.FunctionalTests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expectedContentType = "text/plain";
|
var expectedContentType = "text/plain";
|
||||||
var expectedContent = "Hello, World!";
|
var expectedContent = "Plain text!";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var response = await _client.GetAsync("/plaintext");
|
var response = await _client.GetAsync("/plaintext");
|
||||||
|
|
@ -62,6 +62,25 @@ namespace Microsoft.AspNetCore.Routing.FunctionalTests
|
||||||
Assert.Equal(expectedContent, actualContent);
|
Assert.Equal(expectedContent, actualContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MatchesHelloMiddleware_AndReturnsPlaintext()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedContentType = "text/plain";
|
||||||
|
var expectedContent = "Hello World";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await _client.GetAsync("/helloworld");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.NotNull(response.Content);
|
||||||
|
Assert.NotNull(response.Content.Headers.ContentType);
|
||||||
|
Assert.Equal(expectedContentType, response.Content.Headers.ContentType.MediaType);
|
||||||
|
var actualContent = await response.Content.ReadAsStringAsync();
|
||||||
|
Assert.Equal(expectedContent, actualContent);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task MatchesEndpoint_WithSuccessfulConstraintMatch()
|
public async Task MatchesEndpoint_WithSuccessfulConstraintMatch()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Builder.Internal;
|
using Microsoft.AspNetCore.Builder.Internal;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Internal;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -13,7 +12,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Builder
|
namespace Microsoft.AspNetCore.Builder
|
||||||
{
|
{
|
||||||
public class EndpointRoutingBuilderExtensionsTest
|
public class EndpointRoutingApplicationBuilderExtensionsTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void UseEndpointRouting_ServicesNotRegistered_Throws()
|
public void UseEndpointRouting_ServicesNotRegistered_Throws()
|
||||||
|
|
@ -109,6 +108,26 @@ namespace Microsoft.AspNetCore.Builder
|
||||||
Assert.NotNull(httpContext.Features.Get<IEndpointFeature>());
|
Assert.NotNull(httpContext.Features.Get<IEndpointFeature>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UseEndpointRouting_CallWithBuilder_SetsEndpointBuilder()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var services = CreateServices();
|
||||||
|
|
||||||
|
var app = new ApplicationBuilder(services);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
app.UseEndpointRouting(builder =>
|
||||||
|
{
|
||||||
|
builder.MapEndpoint(d => null, "/", "Test endpoint");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var dataSourceBuilder = (DefaultEndpointDataSourceBuilder)services.GetRequiredService<EndpointDataSourceBuilder>();
|
||||||
|
var endpointBuilder = Assert.Single(dataSourceBuilder.Endpoints);
|
||||||
|
Assert.Equal("Test endpoint", endpointBuilder.DisplayName);
|
||||||
|
}
|
||||||
|
|
||||||
private IServiceProvider CreateServices()
|
private IServiceProvider CreateServices()
|
||||||
{
|
{
|
||||||
var services = new ServiceCollection();
|
var services = new ServiceCollection();
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
// 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.Text;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.AspNetCore.Routing.Matching;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Builder
|
||||||
|
{
|
||||||
|
public class MapEndpointEndpointDataSourceBuilderExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MapEndpoint_StringPattern_BuildsEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new DefaultEndpointDataSourceBuilder();
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker = (d) => null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var endpointBuilder = builder.MapEndpoint(invoker, "/", "Display name!");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(endpointBuilder, Assert.Single(builder.Endpoints));
|
||||||
|
Assert.Equal(invoker, endpointBuilder.Invoker);
|
||||||
|
Assert.Equal("Display name!", endpointBuilder.DisplayName);
|
||||||
|
Assert.Equal("/", endpointBuilder.RoutePattern.RawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapEndpoint_TypedPattern_BuildsEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var builder = new DefaultEndpointDataSourceBuilder();
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker = (d) => null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var endpointBuilder = builder.MapEndpoint(invoker, RoutePatternFactory.Parse("/"), "Display name!");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(endpointBuilder, Assert.Single(builder.Endpoints));
|
||||||
|
Assert.Equal(invoker, endpointBuilder.Invoker);
|
||||||
|
Assert.Equal("Display name!", endpointBuilder.DisplayName);
|
||||||
|
Assert.Equal("/", endpointBuilder.RoutePattern.RawText);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapEndpoint_StringPatternAndMetadata_BuildsEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadata = new object();
|
||||||
|
var builder = new DefaultEndpointDataSourceBuilder();
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker = (d) => null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var endpointBuilder = builder.MapEndpoint(invoker, "/", "Display name!", new[] { metadata });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(endpointBuilder, Assert.Single(builder.Endpoints));
|
||||||
|
Assert.Equal(invoker, endpointBuilder.Invoker);
|
||||||
|
Assert.Equal("Display name!", endpointBuilder.DisplayName);
|
||||||
|
Assert.Equal("/", endpointBuilder.RoutePattern.RawText);
|
||||||
|
Assert.Equal(metadata, Assert.Single(endpointBuilder.Metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MapEndpoint_TypedPatternAndMetadata_BuildsEndpoint()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var metadata = new object();
|
||||||
|
var builder = new DefaultEndpointDataSourceBuilder();
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker = (d) => null;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var endpointBuilder = builder.MapEndpoint(invoker, RoutePatternFactory.Parse("/"), "Display name!", new[] { metadata });
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(endpointBuilder, Assert.Single(builder.Endpoints));
|
||||||
|
Assert.Equal(invoker, endpointBuilder.Invoker);
|
||||||
|
Assert.Equal("Display name!", endpointBuilder.DisplayName);
|
||||||
|
Assert.Equal("/", endpointBuilder.RoutePattern.RawText);
|
||||||
|
Assert.Equal(metadata, Assert.Single(endpointBuilder.Metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// 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.Text;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing.Patterns;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
|
{
|
||||||
|
public class MatcherEndpointBuilderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Build_AllValuesSet_EndpointCreated()
|
||||||
|
{
|
||||||
|
const int defaultOrder = 0;
|
||||||
|
object metadata = new object();
|
||||||
|
Func<RequestDelegate, RequestDelegate> invoker = (d) => null;
|
||||||
|
|
||||||
|
var builder = new MatcherEndpointBuilder(invoker, RoutePatternFactory.Parse("/"), defaultOrder)
|
||||||
|
{
|
||||||
|
DisplayName = "Display name!",
|
||||||
|
Metadata = { metadata }
|
||||||
|
};
|
||||||
|
|
||||||
|
var endpoint = Assert.IsType<MatcherEndpoint>(builder.Build());
|
||||||
|
Assert.Equal("Display name!", endpoint.DisplayName);
|
||||||
|
Assert.Equal(defaultOrder, endpoint.Order);
|
||||||
|
Assert.Equal(invoker, endpoint.Invoker);
|
||||||
|
Assert.Equal("/", endpoint.RoutePattern.RawText);
|
||||||
|
Assert.Equal(metadata, Assert.Single(endpoint.Metadata));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue