Add Addresses and link generation
This commit is contained in:
parent
475712d613
commit
7685e17e80
|
|
@ -1,21 +0,0 @@
|
|||
// 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.Dispatcher;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
||||
namespace DispatcherSample
|
||||
{
|
||||
public class RouteValueAddress : Address
|
||||
{
|
||||
public RouteValueAddress(string displayName, RouteValueDictionary dictionary)
|
||||
{
|
||||
DisplayName = displayName;
|
||||
RouteValueDictionary = dictionary;
|
||||
}
|
||||
|
||||
public override string DisplayName { get; }
|
||||
|
||||
public RouteValueDictionary RouteValueDictionary { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// 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.Routing;
|
||||
|
||||
namespace DispatcherSample
|
||||
{
|
||||
public class RouteValueAddressTable
|
||||
{
|
||||
public IList<RouteValueAddress> Addresses
|
||||
{
|
||||
get
|
||||
{
|
||||
var addresses = new List<RouteValueAddress>
|
||||
{
|
||||
new RouteValueAddress("Mickey", new RouteValueDictionary (new { Character = "Mickey" })),
|
||||
new RouteValueAddress("Hakuna Matata", new RouteValueDictionary (new { Movie = "The Lion King"})),
|
||||
new RouteValueAddress("Simba", new RouteValueDictionary (new { Movie = "The Lion King", Character = "Simba" })),
|
||||
new RouteValueAddress("Mufasa", new RouteValueDictionary (new { Movie = "The Lion King", Character = "Mufasa" })),
|
||||
new RouteValueAddress("Aladdin", new RouteValueDictionary (new { Movie = "Aladdin", Character = "Genie" })),
|
||||
};
|
||||
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,13 @@ namespace DispatcherSample
|
|||
{
|
||||
options.Dispatchers.Add(new RouteTemplateDispatcher("{controller=Home}/{action=Index}/{id?}", ConstraintResolver)
|
||||
{
|
||||
Addresses =
|
||||
{
|
||||
new DispatcherValueAddress(new { controller = "Home", action = "Index", }, new object[]{ new RouteTemplateMetadata("{controller=Home}/{action=Index}/{id?}"), }, "Home:Index()"),
|
||||
new DispatcherValueAddress(new { controller = "Home", action = "About", }, new object[]{ new RouteTemplateMetadata("{controller=Home}/{action=Index}/{id?}"), }, "Home:About()"),
|
||||
new DispatcherValueAddress(new { controller = "Admin", action = "Index", }, new object[]{ new RouteTemplateMetadata("{controller=Home}/{action=Index}/{id?}"), }, "Admin:Index()"),
|
||||
new DispatcherValueAddress(new { controller = "Admin", action = "Users", }, new object[]{ new RouteTemplateMetadata("{controller=Home}/{action=Index}/{id?}"), }, "Admin:GetUsers()/Admin:EditUsers()"),
|
||||
},
|
||||
Endpoints =
|
||||
{
|
||||
new SimpleEndpoint(Home_Index, Array.Empty<object>(), new { controller = "Home", action = "Index", }, "Home:Index()"),
|
||||
|
|
@ -43,14 +50,14 @@ namespace DispatcherSample
|
|||
new DispatcherValueEndpointSelector(),
|
||||
new HttpMethodEndpointSelector(),
|
||||
}
|
||||
}.InvokeAsync);
|
||||
});
|
||||
|
||||
options.HandlerFactories.Add((endpoint) => (endpoint as SimpleEndpoint)?.HandlerFactory);
|
||||
});
|
||||
|
||||
services.AddSingleton<UrlGenerator>();
|
||||
services.AddSingleton<RouteValueAddressTable>();
|
||||
services.AddDispatcher();
|
||||
services.AddRouting();
|
||||
services.AddSingleton<RouteTemplateUrlGenerator>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
|
||||
|
|
@ -85,12 +92,15 @@ namespace DispatcherSample
|
|||
|
||||
public static Task Home_Index(HttpContext httpContext)
|
||||
{
|
||||
var urlGenerator = httpContext.RequestServices.GetService<UrlGenerator>();
|
||||
var url = urlGenerator.GenerateURL(new RouteValueDictionary(new { Movie = "The Lion King", Character = "Mufasa" }), httpContext);
|
||||
var url = httpContext.RequestServices.GetService<RouteTemplateUrlGenerator>();
|
||||
return httpContext.Response.WriteAsync(
|
||||
$"<html>" +
|
||||
$"<body>" +
|
||||
$"<p>Generated url: {url}</p>" +
|
||||
$"<h1>Some links you can visit</h1>" +
|
||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Home", action = "Index", })}\">Home:Index()</a></p>" +
|
||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Home", action = "About", })}\">Home:About()</a></p>" +
|
||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Admin", action = "Index", })}\">Admin:Index()</a></p>" +
|
||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Admin", action = "Users", })}\">Admin:GetUsers()/Admin:EditUsers()</a></p>" +
|
||||
$"</body>" +
|
||||
$"</html>");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,58 +0,0 @@
|
|||
// 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;
|
||||
|
||||
namespace DispatcherSample
|
||||
{
|
||||
public class UrlGenerator
|
||||
{
|
||||
private readonly RouteValueAddressTable _addressTable;
|
||||
|
||||
public UrlGenerator(RouteValueAddressTable addressTable)
|
||||
{
|
||||
_addressTable = addressTable;
|
||||
}
|
||||
|
||||
//Find match from values to a template
|
||||
public string GenerateURL(RouteValueDictionary routeValues, HttpContext context)
|
||||
{
|
||||
var address = FindAddress(_addressTable, routeValues);
|
||||
return $"RouteName: {address.DisplayName} URL: /{address.RouteValueDictionary["Character"]}/{address.RouteValueDictionary["Movie"]}";
|
||||
}
|
||||
|
||||
//Look up the Addresses table
|
||||
private RouteValueAddress FindAddress(RouteValueAddressTable addressTable, RouteValueDictionary routeValues)
|
||||
{
|
||||
var addressMatch = new RouteValueAddress(null, new RouteValueDictionary());
|
||||
foreach (var address in addressTable.Addresses)
|
||||
{
|
||||
foreach (var key in address.RouteValueDictionary.Keys)
|
||||
{
|
||||
if (!routeValues.Keys.Contains(key))
|
||||
{
|
||||
addressMatch.RouteValueDictionary.Clear();
|
||||
break;
|
||||
}
|
||||
|
||||
if (routeValues.Values.Contains(address.RouteValueDictionary[key]))
|
||||
{
|
||||
addressMatch.RouteValueDictionary[key] = routeValues[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (addressMatch.RouteValueDictionary.Count == routeValues.Count)
|
||||
{
|
||||
return new RouteValueAddress(address.DisplayName, address.RouteValueDictionary);
|
||||
}
|
||||
else
|
||||
{
|
||||
addressMatch.RouteValueDictionary.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
return addressMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +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;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
[DebuggerDisplay("{DisplayName,nq}")]
|
||||
public abstract class Address
|
||||
{
|
||||
public abstract string DisplayName { get; }
|
||||
|
||||
public abstract IReadOnlyList<object> Metadata { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher.Abstractions
|
||||
{
|
||||
class AddressGroup
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.Dispatcher
|
||||
{
|
||||
public abstract class AddressTable
|
||||
{
|
||||
public abstract IReadOnlyList<IReadOnlyList<Address>> AddressGroups { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,9 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
[DebuggerDisplay("{DisplayName,nq}")]
|
||||
public abstract class Endpoint
|
||||
{
|
||||
public abstract string DisplayName { get; }
|
||||
|
|
|
|||
|
|
@ -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.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public interface IAddressCollectionProvider
|
||||
{
|
||||
IReadOnlyList<Address> Addresses { get; }
|
||||
|
||||
IChangeToken ChangeToken { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public interface IEndpointCollectionProvider
|
||||
{
|
||||
IReadOnlyList<Endpoint> Endpoints { get; }
|
||||
|
||||
IChangeToken ChangeToken { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public class DefaultAddressTable : AddressTable
|
||||
{
|
||||
private readonly DispatcherOptions _options;
|
||||
private readonly List<Address>[] _groups;
|
||||
|
||||
public DefaultAddressTable(IOptions<DispatcherOptions> options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
_options = options.Value;
|
||||
|
||||
_groups = new List<Address>[options.Value.Dispatchers.Count];
|
||||
for (var i = 0; i < options.Value.Dispatchers.Count; i++)
|
||||
{
|
||||
_groups[i] = new List<Address>(options.Value.Dispatchers[i].AddressProvider?.Addresses ?? Array.Empty<Address>());
|
||||
}
|
||||
}
|
||||
|
||||
public override IReadOnlyList<IReadOnlyList<Address>> AddressGroups => _groups;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,13 +6,29 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public abstract class DispatcherBase
|
||||
public abstract class DispatcherBase : IAddressCollectionProvider, IEndpointCollectionProvider
|
||||
{
|
||||
private IList<Endpoint> _endpoints;
|
||||
private IList<EndpointSelector> _endpointSelectors;
|
||||
private List<Address> _addresses;
|
||||
private List<Endpoint> _endpoints;
|
||||
private List<EndpointSelector> _endpointSelectors;
|
||||
|
||||
public virtual IList<Address> Addresses
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_addresses == null)
|
||||
{
|
||||
_addresses = new List<Address>();
|
||||
}
|
||||
|
||||
return _addresses;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IList<Endpoint> Endpoints
|
||||
{
|
||||
|
|
@ -40,6 +56,12 @@ namespace Microsoft.AspNetCore.Dispatcher
|
|||
}
|
||||
}
|
||||
|
||||
public IChangeToken ChangeToken => NullChangeToken.Singleton;
|
||||
|
||||
IReadOnlyList<Address> IAddressCollectionProvider.Addresses => _addresses;
|
||||
|
||||
IReadOnlyList<Endpoint> IEndpointCollectionProvider.Endpoints => _endpoints;
|
||||
|
||||
public virtual async Task InvokeAsync(HttpContext httpContext)
|
||||
{
|
||||
if (httpContext == null)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
// 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.ObjectModel;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public class DispatcherCollection : Collection<DispatcherEntry>
|
||||
{
|
||||
public void Add(DispatcherBase dispatcher)
|
||||
{
|
||||
if (dispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dispatcher));
|
||||
}
|
||||
|
||||
Add(new DispatcherEntry()
|
||||
{
|
||||
Dispatcher = dispatcher.InvokeAsync,
|
||||
AddressProvider = dispatcher,
|
||||
EndpointProvider = dispatcher,
|
||||
});
|
||||
}
|
||||
|
||||
public void Add(RequestDelegate dispatcher)
|
||||
{
|
||||
if (dispatcher == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(dispatcher));
|
||||
}
|
||||
|
||||
Add(new DispatcherEntry()
|
||||
{
|
||||
Dispatcher = dispatcher,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 Microsoft.AspNetCore.Dispatcher.Abstractions;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public class DispatcherEntry
|
||||
{
|
||||
public RequestDelegate Dispatcher { get; set; }
|
||||
|
||||
public IAddressCollectionProvider AddressProvider { get; set; }
|
||||
|
||||
public IEndpointCollectionProvider EndpointProvider { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
|||
|
||||
foreach (var entry in _options.Dispatchers)
|
||||
{
|
||||
await entry(httpContext);
|
||||
await entry.Dispatcher(httpContext);
|
||||
if (feature.Endpoint != null || feature.RequestDelegate != null)
|
||||
{
|
||||
_logger.LogInformation("Matched endpoint {Endpoint}", feature.Endpoint.DisplayName);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
// 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 System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public class DispatcherOptions
|
||||
{
|
||||
public IList<RequestDelegate> Dispatchers { get; } = new List<RequestDelegate>();
|
||||
public DispatcherCollection Dispatchers { get; } = new DispatcherCollection();
|
||||
|
||||
public IList<EndpointHandlerFactory> HandlerFactories { get; } = new List<EndpointHandlerFactory>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
|
||||
services.AddSingleton<IStartupFilter, DispatcherEndpointStartupFilter>();
|
||||
services.AddSingleton<AddressTable, DefaultAddressTable>();
|
||||
services.AddSingleton<DispatcherValueAddressSelector>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
public class DispatcherValueAddress : Address
|
||||
{
|
||||
public DispatcherValueAddress(object values)
|
||||
: this(values, Array.Empty<object>(), null)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public DispatcherValueAddress(object values, IEnumerable<object> metadata)
|
||||
: this(values, metadata, null)
|
||||
{
|
||||
}
|
||||
|
||||
public DispatcherValueAddress(object values, IEnumerable<object> metadata, string displayName)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(metadata));
|
||||
}
|
||||
|
||||
Values = new DispatcherValueCollection(values);
|
||||
Metadata = metadata.ToArray();
|
||||
DisplayName = displayName;
|
||||
}
|
||||
|
||||
public override string DisplayName { get; }
|
||||
|
||||
public override IReadOnlyList<object> Metadata { get; }
|
||||
|
||||
public DispatcherValueCollection Values { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Dispatcher
|
||||
{
|
||||
// This isn't a proposed design, just a placeholder to demonstrate that things are wired up correctly.
|
||||
public class DispatcherValueAddressSelector
|
||||
{
|
||||
private readonly AddressTable _addressTable;
|
||||
|
||||
public DispatcherValueAddressSelector(AddressTable addressTable)
|
||||
{
|
||||
if (addressTable == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(addressTable));
|
||||
}
|
||||
|
||||
_addressTable = addressTable;
|
||||
}
|
||||
|
||||
public Address SelectAddress(DispatcherValueCollection values)
|
||||
{
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
// Capture the current state so we don't see partial updates.
|
||||
var groups = _addressTable.AddressGroups;
|
||||
for (var i = 0; i < groups.Count; i++)
|
||||
{
|
||||
var matches = new List<Address>();
|
||||
var group = groups[i];
|
||||
|
||||
for (var j = 0; j < group.Count; j++)
|
||||
{
|
||||
var address = group[j] as DispatcherValueAddress;
|
||||
if (address == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsMatch(address, values))
|
||||
{
|
||||
matches.Add(address);
|
||||
}
|
||||
}
|
||||
|
||||
switch (matches.Count)
|
||||
{
|
||||
case 0:
|
||||
// No match, keep going.
|
||||
break;
|
||||
|
||||
case 1:
|
||||
return matches[0];
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException("Ambiguous bro!");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsMatch(DispatcherValueAddress address, DispatcherValueCollection values)
|
||||
{
|
||||
foreach (var kvp in address.Values)
|
||||
{
|
||||
values.TryGetValue(kvp.Key, out var value);
|
||||
|
||||
if (!string.Equals(Convert.ToString(kvp.Value) ?? string.Empty, Convert.ToString(value) ?? string.Empty, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// 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.Dispatcher;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Dispatcher
|
||||
{
|
||||
public interface IRouteTemplateMetadata
|
||||
{
|
||||
string RouteTemplate { get; }
|
||||
|
||||
DispatcherValueCollection Defaults { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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.AspNetCore.Dispatcher;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Dispatcher
|
||||
{
|
||||
public class RouteTemplateMetadata : IRouteTemplateMetadata
|
||||
{
|
||||
public RouteTemplateMetadata(string routeTemplate)
|
||||
: this(routeTemplate, null)
|
||||
{
|
||||
}
|
||||
|
||||
public RouteTemplateMetadata(string routeTemplate, object defaults)
|
||||
{
|
||||
if (routeTemplate == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(routeTemplate));
|
||||
}
|
||||
|
||||
RouteTemplate = routeTemplate;
|
||||
Defaults = new DispatcherValueCollection(defaults);
|
||||
}
|
||||
|
||||
public string RouteTemplate { get; }
|
||||
|
||||
public DispatcherValueCollection Defaults { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
// 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.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Dispatcher;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Routing.Internal;
|
||||
using Microsoft.AspNetCore.Routing.Template;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
|
||||
namespace Microsoft.AspNetCore.Routing.Dispatcher
|
||||
{
|
||||
// This isn't a proposed design, just a placeholder to demonstrate that things are wired up correctly.
|
||||
public class RouteTemplateUrlGenerator
|
||||
{
|
||||
private readonly DispatcherValueAddressSelector _addressSelector;
|
||||
private readonly ObjectPool<UriBuildingContext> _pool;
|
||||
private readonly UrlEncoder _urlEncoder;
|
||||
|
||||
public RouteTemplateUrlGenerator(DispatcherValueAddressSelector addressSelector, UrlEncoder urlEncoder, ObjectPool<UriBuildingContext> pool)
|
||||
{
|
||||
_addressSelector = addressSelector;
|
||||
_urlEncoder = urlEncoder;
|
||||
_pool = pool;
|
||||
}
|
||||
|
||||
public string GenerateUrl(HttpContext httpContext, object values)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (values == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(values));
|
||||
}
|
||||
|
||||
var address = _addressSelector.SelectAddress(new DispatcherValueCollection(values));
|
||||
if (address == null)
|
||||
{
|
||||
throw new InvalidOperationException("Can't find address");
|
||||
}
|
||||
|
||||
var (template, defaults) = GetRouteTemplate(address);
|
||||
var binder = new TemplateBinder(_urlEncoder, _pool, TemplateParser.Parse(template), defaults.AsRouteValueDictionary());
|
||||
|
||||
var feature = httpContext.Features.Get<IDispatcherFeature>();
|
||||
var result = binder.GetValues(feature.Values.AsRouteValueDictionary(), new RouteValueDictionary(values));
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return binder.BindValues(result.AcceptedValues);
|
||||
}
|
||||
private (string, DispatcherValueCollection) GetRouteTemplate(Address address)
|
||||
{
|
||||
for (var i = address.Metadata.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var metadata = address.Metadata[i] as IRouteTemplateMetadata;
|
||||
if (metadata != null)
|
||||
{
|
||||
return (metadata.RouteTemplate, metadata.Defaults);
|
||||
}
|
||||
}
|
||||
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue