Skip to content
This repository was archived by the owner on Nov 27, 2018. It is now read-only.

Denormalize required values to nodes #914

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions samples/RoutingSandbox/Framework/FrameworkConfigurationBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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 Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Patterns;

namespace RoutingSandbox.Framework
{
public class FrameworkConfigurationBuilder
{
private readonly FrameworkEndpointDataSource _dataSource;

internal FrameworkConfigurationBuilder(FrameworkEndpointDataSource dataSource)
{
_dataSource = dataSource;
}

public void AddPattern(string pattern)
{
AddPattern(RoutePatternFactory.Parse(pattern));
}

public void AddPattern(RoutePattern pattern)
{
_dataSource.Patterns.Add(pattern);
}

public void AddHubMethod(string hub, string method, RequestDelegate requestDelegate)
{
_dataSource.HubMethods.Add(new HubMethod
{
Hub = hub,
Method = method,
RequestDelegate = requestDelegate
});
}
}
}
100 changes: 100 additions & 0 deletions samples/RoutingSandbox/Framework/FrameworkEndpointDataSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// 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.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;

namespace RoutingSandbox.Framework
{
internal class FrameworkEndpointDataSource : EndpointDataSource, IEndpointConventionBuilder
{
private readonly RoutePatternTransformer _routePatternTransformer;
private readonly List<Action<EndpointModel>> _conventions;

public List<RoutePattern> Patterns { get; }
public List<HubMethod> HubMethods { get; }

private List<Endpoint> _endpoints;

public FrameworkEndpointDataSource(RoutePatternTransformer routePatternTransformer)
{
_routePatternTransformer = routePatternTransformer;
_conventions = new List<Action<EndpointModel>>();

Patterns = new List<RoutePattern>();
HubMethods = new List<HubMethod>();
}

public override IReadOnlyList<Endpoint> Endpoints
{
get
{
if (_endpoints == null)
{
_endpoints = BuildEndpoints();
}

return _endpoints;
}
}

private List<Endpoint> BuildEndpoints()
{
List<Endpoint> endpoints = new List<Endpoint>();

foreach (var hubMethod in HubMethods)
{
var requiredValues = new { hub = hubMethod.Hub, method = hubMethod.Method };
var order = 1;

foreach (var pattern in Patterns)
{
var resolvedPattern = _routePatternTransformer.SubstituteRequiredValues(pattern, requiredValues);
if (resolvedPattern == null)
{
continue;
}

var endpointModel = new RouteEndpointModel(
hubMethod.RequestDelegate,
resolvedPattern,
order++);
endpointModel.DisplayName = $"{hubMethod.Hub}.{hubMethod.Method}";

foreach (var convention in _conventions)
{
convention(endpointModel);
}

endpoints.Add(endpointModel.Build());
}
}

return endpoints;
}

public override IChangeToken GetChangeToken()
{
return NullChangeToken.Singleton;
}

public void Apply(Action<EndpointModel> convention)
{
_conventions.Add(convention);
}
}

internal class HubMethod
{
public string Hub { get; set; }
public string Method { get; set; }
public RequestDelegate RequestDelegate { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;

namespace RoutingSandbox.Framework
{
public static class FrameworkEndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapFramework(this IEndpointRouteBuilder builder, Action<FrameworkConfigurationBuilder> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}

var dataSource = builder.ServiceProvider.GetRequiredService<FrameworkEndpointDataSource>();

var configurationBuilder = new FrameworkConfigurationBuilder(dataSource);
configure(configurationBuilder);

builder.DataSources.Add(dataSource);

return dataSource;
}
}
}
2 changes: 1 addition & 1 deletion samples/RoutingSandbox/RoutingSandbox.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
Expand Down
18 changes: 18 additions & 0 deletions samples/RoutingSandbox/SlugifyParameterTransformer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.Text.RegularExpressions;
using Microsoft.AspNetCore.Routing;

namespace RoutingSandbox
{
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
{
// Slugify value
return value == null ? null : Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2", RegexOptions.None, TimeSpan.FromMilliseconds(100)).ToLower();
}
}
}
21 changes: 18 additions & 3 deletions samples/RoutingSandbox/UseEndpointRoutingStartup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using RoutingSandbox.Framework;

namespace RoutingSandbox
{
Expand All @@ -23,7 +24,11 @@ public class UseEndpointRoutingStartup

public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddRouting(options =>
{
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
});
services.AddSingleton<FrameworkEndpointDataSource>();
}

public void Configure(IApplicationBuilder app)
Expand Down Expand Up @@ -72,12 +77,22 @@ public void Configure(IApplicationBuilder app)
using (var writer = new StreamWriter(httpContext.Response.Body, Encoding.UTF8, 1024, leaveOpen: true))
{
var graphWriter = httpContext.RequestServices.GetRequiredService<DfaGraphWriter>();
var dataSource = httpContext.RequestServices.GetRequiredService<CompositeEndpointDataSource>();
var dataSource = httpContext.RequestServices.GetRequiredService<EndpointDataSource>();
graphWriter.Write(dataSource, writer);
}

return Task.CompletedTask;
});

builder.MapFramework(frameworkBuilder =>
{
frameworkBuilder.AddPattern("/transform/{hub:slugify=TestHub}/{method:slugify=TestMethod}");
frameworkBuilder.AddPattern("/{hub}/{method=TestMethod}");

frameworkBuilder.AddHubMethod("TestHub", "TestMethod", context => context.Response.WriteAsync("TestMethod!"));
frameworkBuilder.AddHubMethod("Login", "Authenticate", context => context.Response.WriteAsync("Authenticate!"));
frameworkBuilder.AddHubMethod("Login", "Logout", context => context.Response.WriteAsync("Logout!"));
});
});

app.UseStaticFiles();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,10 +170,11 @@ private string DebuggerDisplayString
var routeValuesAddressMetadata = routeEndpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>();
sb.Append(", Route Name: ");
sb.Append(routeValuesAddressMetadata?.RouteName);
if (routeValuesAddressMetadata?.RequiredValues != null)
var routeValues = routeEndpoint.ResolveRouteValues();
if (routeValues?.Count > 0)
{
sb.Append(", Required Values: new { ");
sb.Append(string.Join(", ", FormatValues(routeValuesAddressMetadata.RequiredValues)));
sb.Append(string.Join(", ", FormatValues(routeValues)));
sb.Append(" }");
}
sb.Append(", Order: ");
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.AspNetCore.Routing/DefaultLinkGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ private TemplateBinder CreateTemplateBinder(RouteEndpoint endpoint)
_uriBuildingContextPool,
endpoint.RoutePattern,
new RouteValueDictionary(endpoint.RoutePattern.Defaults),
endpoint.Metadata.GetMetadata<IRouteValuesAddressMetadata>()?.RequiredValues.Keys,
endpoint.ResolveRouteValues()?.Keys,
policies);
}

Expand Down
23 changes: 23 additions & 0 deletions src/Microsoft.AspNetCore.Routing/Internal/RequiredValueHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// 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.Routing.Patterns;

namespace Microsoft.AspNetCore.Routing.Internal
{
internal static class RequiredValueHelpers
{
public static bool TryGetRequiredValue(RoutePattern routePattern, RoutePatternParameterPart parameterPart, out object value)
{
if (!routePattern.RequiredValues.TryGetValue(parameterPart.Name, out value))
{
return false;
}

return !RouteValueEqualityComparer.Default.Equals(value, string.Empty);
}
}
}
Loading