Add Template abstraction
This change adds the Template as a top level abstraction. URL templating is now a two-stage process. First you use a 'key' to look up a Template, then you use the Template to create the URL. This change also has some cleanup of the way RoutePatternBinder gets instantiated. I added a factory service so that most of the complex things can be made internal to Dispatcher. Now it's much easier to constuct and use. These impacts some pubternal APIs that we already broke, but makes them actually nice :) Also cleaned up some tests and fixed one that was broken and not running.
This commit is contained in:
parent
2d661396df
commit
736b49294d
|
|
@ -2,13 +2,11 @@
|
||||||
// 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;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
|
||||||
using Microsoft.AspNetCore.Routing.Tree;
|
using Microsoft.AspNetCore.Routing.Tree;
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
|
@ -30,8 +28,7 @@ namespace Microsoft.AspNetCore.Dispatcher.Performance
|
||||||
|
|
||||||
var treeBuilder = new TreeRouteBuilder(
|
var treeBuilder = new TreeRouteBuilder(
|
||||||
NullLoggerFactory.Instance,
|
NullLoggerFactory.Instance,
|
||||||
UrlEncoder.Default,
|
new RoutePatternBinderFactory(UrlEncoder.Default, new DefaultObjectPoolProvider()),
|
||||||
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
|
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
|
||||||
|
|
||||||
treeBuilder.MapInbound(handler, Routing.Template.TemplateParser.Parse("api/Widgets"), "default", 0);
|
treeBuilder.MapInbound(handler, Routing.Template.TemplateParser.Parse("api/Widgets"), "default", 0);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
|
|
@ -30,8 +29,7 @@ namespace Microsoft.AspNetCore.Routing.Performance
|
||||||
|
|
||||||
var treeBuilder = new TreeRouteBuilder(
|
var treeBuilder = new TreeRouteBuilder(
|
||||||
NullLoggerFactory.Instance,
|
NullLoggerFactory.Instance,
|
||||||
UrlEncoder.Default,
|
new RoutePatternBinderFactory(UrlEncoder.Default, new DefaultObjectPoolProvider()),
|
||||||
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
|
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
|
||||||
|
|
||||||
treeBuilder.MapInbound(handler, TemplateParser.Parse("api/Widgets"), "default", 0);
|
treeBuilder.MapInbound(handler, TemplateParser.Parse("api/Widgets"), "default", 0);
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ namespace DispatcherSample
|
||||||
|
|
||||||
// This is a temporary layering issue, don't worry about :)
|
// This is a temporary layering issue, don't worry about :)
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
services.AddSingleton<RouteTemplateUrlGenerator>();
|
|
||||||
services.AddSingleton<IDefaultMatcherFactory, TreeMatcherFactory>();
|
services.AddSingleton<IDefaultMatcherFactory, TreeMatcherFactory>();
|
||||||
|
|
||||||
// Imagine this was done by MVC or another framework.
|
// Imagine this was done by MVC or another framework.
|
||||||
|
|
@ -86,15 +85,16 @@ namespace DispatcherSample
|
||||||
|
|
||||||
public static Task Home_Index(HttpContext httpContext)
|
public static Task Home_Index(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
var url = httpContext.RequestServices.GetService<RouteTemplateUrlGenerator>();
|
var templateFactory = httpContext.RequestServices.GetRequiredService<TemplateFactory>();
|
||||||
|
|
||||||
return httpContext.Response.WriteAsync(
|
return httpContext.Response.WriteAsync(
|
||||||
$"<html>" +
|
$"<html>" +
|
||||||
$"<body>" +
|
$"<body>" +
|
||||||
$"<h1>Some links you can visit</h1>" +
|
$"<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=\"{templateFactory.GetTemplate(new { controller = "Home", action = "Index", }).GetUrl(httpContext)}\">Home:Index()</a></p>" +
|
||||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Home", action = "About", })}\">Home:About()</a></p>" +
|
$"<p><a href=\"{templateFactory.GetTemplate(new { controller = "Home", action = "About", }).GetUrl(httpContext)}\">Home:About()</a></p>" +
|
||||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Admin", action = "Index", })}\">Admin:Index()</a></p>" +
|
$"<p><a href=\"{templateFactory.GetTemplate(new { controller = "Admin", action = "Index", }).GetUrl(httpContext)}\">Admin:Index()</a></p>" +
|
||||||
$"<p><a href=\"{url.GenerateUrl(httpContext, new { controller = "Admin", action = "Users", })}\">Admin:GetUsers()/Admin:EditUsers()</a></p>" +
|
$"<p><a href=\"{templateFactory.GetTemplate(new { controller = "Admin", action = "Users", }).GetUrl(httpContext)}\">Admin:GetUsers()/Admin:EditUsers()</a></p>" +
|
||||||
$"</body>" +
|
$"</body>" +
|
||||||
$"</html>");
|
$"</html>");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher.Abstractions
|
|
||||||
{
|
|
||||||
class AddressGroup
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public abstract class Template
|
||||||
|
{
|
||||||
|
public virtual string GetUrl()
|
||||||
|
{
|
||||||
|
return GetUrl(null, new DispatcherValueCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetUrl(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
return GetUrl(httpContext, new DispatcherValueCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string GetUrl(DispatcherValueCollection values)
|
||||||
|
{
|
||||||
|
return GetUrl(null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract string GetUrl(HttpContext httpContext, DispatcherValueCollection values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
public struct BufferValue
|
internal struct BufferValue
|
||||||
{
|
{
|
||||||
public BufferValue(string value, bool requiresEncoding)
|
public BufferValue(string value, bool requiresEncoding)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
// 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 DefaultTemplateFactory : TemplateFactory
|
||||||
|
{
|
||||||
|
private readonly ITemplateFactoryComponent[] _components;
|
||||||
|
|
||||||
|
public DefaultTemplateFactory(IEnumerable<ITemplateFactoryComponent> components)
|
||||||
|
{
|
||||||
|
if (components == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(components));
|
||||||
|
}
|
||||||
|
|
||||||
|
_components = components.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Template GetTemplateFromKey<TKey>(TKey key)
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _components.Length; i++)
|
||||||
|
{
|
||||||
|
var component = _components[i] as TemplateFactory<TKey>;
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var template = component.GetTemplate(key);
|
||||||
|
if (template != null)
|
||||||
|
{
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,17 +25,18 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// Adds a default dispatcher which will collect all data sources and endpoint selectors from DI.
|
// Adds a default dispatcher which will collect all data sources and endpoint selectors from DI.
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<DispatcherOptions>, DefaultDispatcherConfigureOptions>());
|
services.TryAddEnumerable(ServiceDescriptor.Transient<IConfigureOptions<DispatcherOptions>, DefaultDispatcherConfigureOptions>());
|
||||||
|
|
||||||
services.AddSingleton<AddressTable, DefaultAddressTable>();
|
//
|
||||||
services.AddSingleton<TemplateAddressSelector>();
|
// Addresses + Templates
|
||||||
|
//
|
||||||
|
services.TryAddSingleton<AddressTable, DefaultAddressTable>();
|
||||||
|
services.TryAddSingleton<TemplateFactory, DefaultTemplateFactory>();
|
||||||
|
services.TryAddSingleton<ITemplateFactoryComponent, RoutePatternTemplateFactory>();
|
||||||
|
services.TryAddSingleton<TemplateAddressSelector>();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Infrastructure
|
// Misc Infrastructure
|
||||||
//
|
//
|
||||||
services.AddSingleton<ObjectPool<UriBuildingContext>>(s =>
|
services.TryAddSingleton<RoutePatternBinderFactory>();
|
||||||
{
|
|
||||||
var provider = s.GetRequiredService<ObjectPoolProvider>();
|
|
||||||
return provider.Create<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy());
|
|
||||||
});
|
|
||||||
|
|
||||||
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHandlerFactory, TemplateEndpointHandlerFactory>());
|
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHandlerFactory, TemplateEndpointHandlerFactory>());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
string Template { get; }
|
string Template { get; }
|
||||||
|
|
||||||
DispatcherValueCollection Values { get; }
|
DispatcherValueCollection Defaults { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
// 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 Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public interface ITemplateFactoryComponent
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,10 +20,10 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
private readonly DispatcherValueCollection _filters;
|
private readonly DispatcherValueCollection _filters;
|
||||||
private readonly RoutePattern _pattern;
|
private readonly RoutePattern _pattern;
|
||||||
|
|
||||||
public RoutePatternBinder(
|
internal RoutePatternBinder(
|
||||||
UrlEncoder urlEncoder,
|
UrlEncoder urlEncoder,
|
||||||
ObjectPool<UriBuildingContext> pool,
|
ObjectPool<UriBuildingContext> pool,
|
||||||
RoutePattern template,
|
RoutePattern pattern,
|
||||||
DispatcherValueCollection defaults)
|
DispatcherValueCollection defaults)
|
||||||
{
|
{
|
||||||
if (urlEncoder == null)
|
if (urlEncoder == null)
|
||||||
|
|
@ -36,14 +36,14 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
throw new ArgumentNullException(nameof(pool));
|
throw new ArgumentNullException(nameof(pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template == null)
|
if (pattern == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(template));
|
throw new ArgumentNullException(nameof(pattern));
|
||||||
}
|
}
|
||||||
|
|
||||||
_urlEncoder = urlEncoder;
|
_urlEncoder = urlEncoder;
|
||||||
_pool = pool;
|
_pool = pool;
|
||||||
_pattern = template;
|
_pattern = pattern;
|
||||||
_defaults = defaults;
|
_defaults = defaults;
|
||||||
|
|
||||||
// Any default that doesn't have a corresponding parameter is a 'filter' and if a value
|
// Any default that doesn't have a corresponding parameter is a 'filter' and if a value
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
// 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.Patterns;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public class RoutePatternBinderFactory
|
||||||
|
{
|
||||||
|
private readonly UrlEncoder _encoder;
|
||||||
|
private readonly ObjectPool<UriBuildingContext> _pool;
|
||||||
|
|
||||||
|
public RoutePatternBinderFactory(UrlEncoder encoder, ObjectPoolProvider objectPoolProvider)
|
||||||
|
{
|
||||||
|
if (encoder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(encoder));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectPoolProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(objectPoolProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
_encoder = encoder;
|
||||||
|
_pool = objectPoolProvider.Create(new UriBuilderContextPooledObjectPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinder Create(string pattern)
|
||||||
|
{
|
||||||
|
if (pattern == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Create(RoutePattern.Parse(pattern), new DispatcherValueCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinder Create(string pattern, DispatcherValueCollection defaults)
|
||||||
|
{
|
||||||
|
if (pattern == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaults == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(defaults));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Create(RoutePattern.Parse(pattern), defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinder Create(RoutePattern pattern)
|
||||||
|
{
|
||||||
|
if (pattern == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Create(pattern, new DispatcherValueCollection());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinder Create(RoutePattern pattern, DispatcherValueCollection defaults)
|
||||||
|
{
|
||||||
|
if (pattern == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defaults == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(defaults));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RoutePatternBinder(_encoder, _pool, pattern, defaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy<UriBuildingContext>
|
||||||
|
{
|
||||||
|
public UriBuildingContext Create()
|
||||||
|
{
|
||||||
|
return new UriBuildingContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Return(UriBuildingContext obj)
|
||||||
|
{
|
||||||
|
obj.Clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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.Http;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public class RoutePatternTemplate : Template
|
||||||
|
{
|
||||||
|
private readonly RoutePatternBinder _binder;
|
||||||
|
|
||||||
|
public RoutePatternTemplate(RoutePatternBinder binder)
|
||||||
|
{
|
||||||
|
if (binder == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(binder));
|
||||||
|
}
|
||||||
|
|
||||||
|
_binder = binder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetUrl(DispatcherValueCollection values)
|
||||||
|
{
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetUrl(null, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string GetUrl(HttpContext httpContext, DispatcherValueCollection values)
|
||||||
|
{
|
||||||
|
if (values == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
var ambientValues = GetAmbientValues(httpContext);
|
||||||
|
var result = _binder.GetValues(ambientValues, values);
|
||||||
|
if (result.acceptedValues == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _binder.BindValues(result.acceptedValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DispatcherValueCollection GetAmbientValues(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
if (httpContext == null)
|
||||||
|
{
|
||||||
|
return new DispatcherValueCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
var feature = httpContext.Features.Get<IDispatcherFeature>();
|
||||||
|
if (feature == null)
|
||||||
|
{
|
||||||
|
return new DispatcherValueCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature.Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
internal class RoutePatternTemplateFactory : TemplateFactory<DispatcherValueCollection>
|
||||||
|
{
|
||||||
|
private readonly TemplateAddressSelector _selector;
|
||||||
|
private readonly RoutePatternBinderFactory _binderFactory;
|
||||||
|
|
||||||
|
public RoutePatternTemplateFactory(TemplateAddressSelector selector, RoutePatternBinderFactory binderFactory)
|
||||||
|
{
|
||||||
|
if (selector == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(selector));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (binderFactory == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(binderFactory));
|
||||||
|
}
|
||||||
|
|
||||||
|
_selector = selector;
|
||||||
|
_binderFactory = binderFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Template GetTemplate(DispatcherValueCollection key)
|
||||||
|
{
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
var address = _selector.SelectAddress(key);
|
||||||
|
if (address == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address is ITemplateAddress templateAddress)
|
||||||
|
{
|
||||||
|
var binder = _binderFactory.Create(templateAddress.Template, templateAddress.Defaults);
|
||||||
|
return new RoutePatternTemplate(binder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
// used a value for {p1}, we have to output the entire segment up to the next "/".
|
// used a value for {p1}, we have to output the entire segment up to the next "/".
|
||||||
// Otherwise we could end up with the partial segment "v1" instead of the entire
|
// Otherwise we could end up with the partial segment "v1" instead of the entire
|
||||||
// segment "v1-v2.xml".
|
// segment "v1-v2.xml".
|
||||||
public enum SegmentState
|
internal enum SegmentState
|
||||||
{
|
{
|
||||||
Beginning,
|
Beginning,
|
||||||
Inside,
|
Inside,
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
Template = template;
|
Template = template;
|
||||||
Values = new DispatcherValueCollection(values);
|
Defaults = new DispatcherValueCollection(values);
|
||||||
DisplayName = displayName;
|
DisplayName = displayName;
|
||||||
Metadata = metadata.ToArray();
|
Metadata = metadata.ToArray();
|
||||||
}
|
}
|
||||||
|
|
@ -38,6 +38,6 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
|
||||||
public string Template { get; }
|
public string Template { get; }
|
||||||
|
|
||||||
public DispatcherValueCollection Values { get; }
|
public DispatcherValueCollection Defaults { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
_addressTable = addressTable;
|
_addressTable = addressTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Address SelectAddress(object values)
|
||||||
|
{
|
||||||
|
return SelectAddress(new DispatcherValueCollection(values));
|
||||||
|
}
|
||||||
|
|
||||||
public Address SelectAddress(DispatcherValueCollection values)
|
public Address SelectAddress(DispatcherValueCollection values)
|
||||||
{
|
{
|
||||||
if (values == null)
|
if (values == null)
|
||||||
|
|
@ -69,7 +74,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
|
||||||
private bool IsMatch(ITemplateAddress address, DispatcherValueCollection values)
|
private bool IsMatch(ITemplateAddress address, DispatcherValueCollection values)
|
||||||
{
|
{
|
||||||
foreach (var kvp in address.Values)
|
foreach (var kvp in address.Defaults)
|
||||||
{
|
{
|
||||||
values.TryGetValue(kvp.Key, out var value);
|
values.TryGetValue(kvp.Key, out var value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public abstract class TemplateFactory
|
||||||
|
{
|
||||||
|
public Template GetTemplate(object values)
|
||||||
|
{
|
||||||
|
return GetTemplateFromKey(new DispatcherValueCollection(values));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract Template GetTemplateFromKey<TKey>(TKey key) where TKey : class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public abstract class TemplateFactory<TKey> : ITemplateFactoryComponent
|
||||||
|
{
|
||||||
|
public abstract Template GetTemplate(TKey key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher
|
|
||||||
{
|
|
||||||
public class UriBuilderContextPooledObjectPolicy : IPooledObjectPolicy<UriBuildingContext>
|
|
||||||
{
|
|
||||||
public UriBuildingContext Create()
|
|
||||||
{
|
|
||||||
return new UriBuildingContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Return(UriBuildingContext obj)
|
|
||||||
{
|
|
||||||
obj.Clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -10,7 +10,7 @@ using System.Text.Encodings.Web;
|
||||||
namespace Microsoft.AspNetCore.Dispatcher
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
||||||
public class UriBuildingContext
|
internal class UriBuildingContext
|
||||||
{
|
{
|
||||||
// Holds the 'accepted' parts of the uri.
|
// Holds the 'accepted' parts of the uri.
|
||||||
private readonly StringBuilder _uri;
|
private readonly StringBuilder _uri;
|
||||||
|
|
|
||||||
|
|
@ -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 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 TemplateAddressSelector _addressSelector;
|
|
||||||
private readonly ObjectPool<UriBuildingContext> _pool;
|
|
||||||
private readonly UrlEncoder _urlEncoder;
|
|
||||||
|
|
||||||
public RouteTemplateUrlGenerator(TemplateAddressSelector 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)) as ITemplateAddress;
|
|
||||||
if (address == null)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Can't find address");
|
|
||||||
}
|
|
||||||
|
|
||||||
var binder = new TemplateBinder(_urlEncoder, _pool, Template.TemplateParser.Parse(address.Template), new RouteValueDictionary());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -242,9 +242,8 @@ namespace Microsoft.AspNetCore.Routing
|
||||||
{
|
{
|
||||||
if (_binder == null)
|
if (_binder == null)
|
||||||
{
|
{
|
||||||
var urlEncoder = context.RequestServices.GetRequiredService<UrlEncoder>();
|
var binderFactory = context.RequestServices.GetRequiredService<RoutePatternBinderFactory>();
|
||||||
var pool = context.RequestServices.GetRequiredService<ObjectPool<UriBuildingContext>>();
|
_binder = new TemplateBinder(binderFactory.Create(ParsedTemplate.ToRoutePattern(), Defaults));
|
||||||
_binder = new TemplateBinder(urlEncoder, pool, ParsedTemplate, Defaults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +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;
|
using System;
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNetCore.Dispatcher;
|
using Microsoft.AspNetCore.Dispatcher;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Template
|
namespace Microsoft.AspNetCore.Routing.Template
|
||||||
{
|
{
|
||||||
|
|
@ -12,28 +10,14 @@ namespace Microsoft.AspNetCore.Routing.Template
|
||||||
{
|
{
|
||||||
private readonly RoutePatternBinder _binder;
|
private readonly RoutePatternBinder _binder;
|
||||||
|
|
||||||
public TemplateBinder(
|
public TemplateBinder(RoutePatternBinder binder)
|
||||||
UrlEncoder urlEncoder,
|
|
||||||
ObjectPool<UriBuildingContext> pool,
|
|
||||||
RouteTemplate template,
|
|
||||||
RouteValueDictionary defaults)
|
|
||||||
{
|
{
|
||||||
if (urlEncoder == null)
|
if (binder == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(urlEncoder));
|
throw new ArgumentNullException(nameof(binder));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pool == null)
|
_binder = binder;
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(pool));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (template == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(template));
|
|
||||||
}
|
|
||||||
|
|
||||||
_binder = new RoutePatternBinder(urlEncoder, pool, template.ToRoutePattern(), defaults);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Get the list of values we're going to try to use to match and generate this URI
|
// Step 1: Get the list of values we're going to try to use to match and generate this URI
|
||||||
|
|
|
||||||
|
|
@ -20,21 +20,12 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ILogger _constraintLogger;
|
private readonly ILogger _constraintLogger;
|
||||||
private readonly UrlEncoder _urlEncoder;
|
private readonly RoutePatternBinderFactory _binderFactory;
|
||||||
private readonly ObjectPool<UriBuildingContext> _objectPool;
|
|
||||||
private readonly IInlineConstraintResolver _constraintResolver;
|
private readonly IInlineConstraintResolver _constraintResolver;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of <see cref="TreeRouteBuilder"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
|
|
||||||
/// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param>
|
|
||||||
/// <param name="objectPool">The <see cref="ObjectPool{UrlBuildingContext}"/>.</param>
|
|
||||||
/// <param name="constraintResolver">The <see cref="IInlineConstraintResolver"/>.</param>
|
|
||||||
public TreeRouteBuilder(
|
public TreeRouteBuilder(
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
UrlEncoder urlEncoder,
|
RoutePatternBinderFactory binderFactory,
|
||||||
ObjectPool<UriBuildingContext> objectPool,
|
|
||||||
IInlineConstraintResolver constraintResolver)
|
IInlineConstraintResolver constraintResolver)
|
||||||
{
|
{
|
||||||
if (loggerFactory == null)
|
if (loggerFactory == null)
|
||||||
|
|
@ -42,14 +33,9 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
throw new ArgumentNullException(nameof(loggerFactory));
|
throw new ArgumentNullException(nameof(loggerFactory));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlEncoder == null)
|
if (binderFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(urlEncoder));
|
throw new ArgumentNullException(nameof(binderFactory));
|
||||||
}
|
|
||||||
|
|
||||||
if (objectPool == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(objectPool));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (constraintResolver == null)
|
if (constraintResolver == null)
|
||||||
|
|
@ -57,8 +43,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
throw new ArgumentNullException(nameof(constraintResolver));
|
throw new ArgumentNullException(nameof(constraintResolver));
|
||||||
}
|
}
|
||||||
|
|
||||||
_urlEncoder = urlEncoder;
|
_binderFactory = binderFactory;
|
||||||
_objectPool = objectPool;
|
|
||||||
_constraintResolver = constraintResolver;
|
_constraintResolver = constraintResolver;
|
||||||
|
|
||||||
_logger = loggerFactory.CreateLogger<TreeRouter>();
|
_logger = loggerFactory.CreateLogger<TreeRouter>();
|
||||||
|
|
@ -251,8 +236,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
return new TreeRouter(
|
return new TreeRouter(
|
||||||
trees.Values.OrderBy(tree => tree.Order).ToArray(),
|
trees.Values.OrderBy(tree => tree.Order).ToArray(),
|
||||||
OutboundEntries,
|
OutboundEntries,
|
||||||
_urlEncoder,
|
_binderFactory,
|
||||||
_objectPool,
|
|
||||||
_logger,
|
_logger,
|
||||||
_constraintLogger,
|
_constraintLogger,
|
||||||
version);
|
version);
|
||||||
|
|
|
||||||
|
|
@ -33,22 +33,10 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ILogger _constraintLogger;
|
private readonly ILogger _constraintLogger;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new <see cref="TreeRouter"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="trees">The list of <see cref="UrlMatchingTree"/> that contains the route entries.</param>
|
|
||||||
/// <param name="linkGenerationEntries">The set of <see cref="OutboundRouteEntry"/>.</param>
|
|
||||||
/// <param name="urlEncoder">The <see cref="UrlEncoder"/>.</param>
|
|
||||||
/// <param name="objectPool">The <see cref="ObjectPool{T}"/>.</param>
|
|
||||||
/// <param name="routeLogger">The <see cref="ILogger"/> instance.</param>
|
|
||||||
/// <param name="constraintLogger">The <see cref="ILogger"/> instance used
|
|
||||||
/// in <see cref="RouteConstraintMatcher"/>.</param>
|
|
||||||
/// <param name="version">The version of this route.</param>
|
|
||||||
public TreeRouter(
|
public TreeRouter(
|
||||||
UrlMatchingTree[] trees,
|
UrlMatchingTree[] trees,
|
||||||
IEnumerable<OutboundRouteEntry> linkGenerationEntries,
|
IEnumerable<OutboundRouteEntry> linkGenerationEntries,
|
||||||
UrlEncoder urlEncoder,
|
RoutePatternBinderFactory binderFactory,
|
||||||
ObjectPool<UriBuildingContext> objectPool,
|
|
||||||
ILogger routeLogger,
|
ILogger routeLogger,
|
||||||
ILogger constraintLogger,
|
ILogger constraintLogger,
|
||||||
int version)
|
int version)
|
||||||
|
|
@ -63,14 +51,9 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
throw new ArgumentNullException(nameof(linkGenerationEntries));
|
throw new ArgumentNullException(nameof(linkGenerationEntries));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlEncoder == null)
|
if (binderFactory == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(urlEncoder));
|
throw new ArgumentNullException(nameof(binderFactory));
|
||||||
}
|
|
||||||
|
|
||||||
if (objectPool == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(objectPool));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (routeLogger == null)
|
if (routeLogger == null)
|
||||||
|
|
@ -93,8 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
|
|
||||||
foreach (var entry in linkGenerationEntries)
|
foreach (var entry in linkGenerationEntries)
|
||||||
{
|
{
|
||||||
|
var binder = new TemplateBinder(binderFactory.Create(entry.RouteTemplate.ToRoutePattern(), entry.Defaults));
|
||||||
var binder = new TemplateBinder(urlEncoder, objectPool, entry.RouteTemplate, entry.Defaults);
|
|
||||||
var outboundMatch = new OutboundMatch() { Entry = entry, TemplateBinder = binder };
|
var outboundMatch = new OutboundMatch() { Entry = entry, TemplateBinder = binder };
|
||||||
outboundMatches.Add(outboundMatch);
|
outboundMatches.Add(outboundMatch);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ namespace Microsoft.AspNetCore.Dispatcher.FunctionalTest
|
||||||
|
|
||||||
// This is a temporary layering issue, don't worry about it :)
|
// This is a temporary layering issue, don't worry about it :)
|
||||||
services.AddRouting();
|
services.AddRouting();
|
||||||
services.AddSingleton<RouteTemplateUrlGenerator>();
|
|
||||||
services.AddSingleton<IDefaultMatcherFactory, TreeMatcherFactory>();
|
services.AddSingleton<IDefaultMatcherFactory, TreeMatcherFactory>();
|
||||||
|
|
||||||
services.Configure<DispatcherOptions>(ConfigureDispatcher);
|
services.Configure<DispatcherOptions>(ConfigureDispatcher);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
// 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 Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
public class DefaultTemplateFactoryTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplateFromKey_UsesMatchingComponent_SelectsTemplate()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = Mock.Of<Template>();
|
||||||
|
var factory = new DefaultTemplateFactory(new ITemplateFactoryComponent[]
|
||||||
|
{
|
||||||
|
Mock.Of<TemplateFactory<string>>(f => f.GetTemplate("foo") == expected),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var template = factory.GetTemplateFromKey("foo");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(expected, template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplateFromKey_UsesMatchingComponent_IgnoresOtherComponents()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = Mock.Of<Template>();
|
||||||
|
var factory = new DefaultTemplateFactory(new ITemplateFactoryComponent[]
|
||||||
|
{
|
||||||
|
Mock.Of<TemplateFactory<int>>(f => f.GetTemplate(17) == Mock.Of<Template>()),
|
||||||
|
Mock.Of<TemplateFactory<string>>(f => f.GetTemplate("foo") == expected),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var template = factory.GetTemplateFromKey("foo");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(expected, template);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetTemplateFromKey_UsesMatchingComponent_ReturnsFirstMatch()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = Mock.Of<Template>();
|
||||||
|
var factory = new DefaultTemplateFactory(new ITemplateFactoryComponent[]
|
||||||
|
{
|
||||||
|
Mock.Of<TemplateFactory<string>>(), // Will return null
|
||||||
|
Mock.Of<TemplateFactory<string>>(f => f.GetTemplate("foo") == expected),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var template = factory.GetTemplateFromKey("foo");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(expected, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,16 +6,21 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNetCore.Dispatcher.Patterns;
|
using Microsoft.AspNetCore.Dispatcher.Patterns;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Microsoft.Extensions.WebEncoders.Testing;
|
using Microsoft.Extensions.WebEncoders.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
public class RoutePatternBinderTests
|
public class RoutePatternBinderTest
|
||||||
{
|
{
|
||||||
|
public RoutePatternBinderTest()
|
||||||
|
{
|
||||||
|
BinderFactory = new RoutePatternBinderFactory(new UrlTestEncoder(), new DefaultObjectPoolProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinderFactory BinderFactory { get; }
|
||||||
|
|
||||||
public static TheoryData EmptyAndNullDefaultValues =>
|
public static TheoryData EmptyAndNullDefaultValues =>
|
||||||
new TheoryData<string, DispatcherValueCollection, DispatcherValueCollection, string>
|
new TheoryData<string, DispatcherValueCollection, DispatcherValueCollection, string>
|
||||||
{
|
{
|
||||||
|
|
@ -114,12 +119,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var encoder = new UrlTestEncoder();
|
var binder = BinderFactory.Create(pattern, defaults);
|
||||||
var binder = new RoutePatternBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
RoutePattern.Parse(pattern),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues: null, values: values);
|
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues: null, values: values);
|
||||||
|
|
@ -264,12 +264,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var encoder = new UrlTestEncoder();
|
var binder = BinderFactory.Create(pattern, defaults);
|
||||||
var binder = new RoutePatternBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
RoutePattern.Parse(pattern),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues: ambientValues, values: values);
|
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues: ambientValues, values: values);
|
||||||
|
|
@ -695,11 +690,7 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
// Arrange
|
// Arrange
|
||||||
var pattern = "{area?}/{controller=Home}/{action=Index}/{id?}";
|
var pattern = "{area?}/{controller=Home}/{action=Index}/{id?}";
|
||||||
var encoder = new UrlTestEncoder();
|
var encoder = new UrlTestEncoder();
|
||||||
var binder = new RoutePatternBinder(
|
var binder = BinderFactory.Create(pattern);
|
||||||
new UrlTestEncoder(),
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
RoutePattern.Parse(pattern),
|
|
||||||
defaults: null);
|
|
||||||
var ambientValues = new DispatcherValueCollection();
|
var ambientValues = new DispatcherValueCollection();
|
||||||
var routeValues = new DispatcherValueCollection(new { controller = "Test", action = "Index" });
|
var routeValues = new DispatcherValueCollection(new { controller = "Test", action = "Index" });
|
||||||
|
|
||||||
|
|
@ -1116,7 +1107,41 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static void RunTest(
|
[Theory]
|
||||||
|
[InlineData(null, null, true)]
|
||||||
|
[InlineData("blog", null, false)]
|
||||||
|
[InlineData(null, "store", false)]
|
||||||
|
[InlineData("Cool", "cool", true)]
|
||||||
|
[InlineData("Co0l", "cool", false)]
|
||||||
|
public void RoutePartsEqualTest(object left, object right, bool expected)
|
||||||
|
{
|
||||||
|
// Arrange & Act & Assert
|
||||||
|
if (expected)
|
||||||
|
{
|
||||||
|
Assert.True(RoutePatternBinder.RoutePartsEqual(left, right));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert.False(RoutePatternBinder.RoutePartsEqual(left, right));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunTest(
|
||||||
|
string pattern,
|
||||||
|
object defaults,
|
||||||
|
object ambientValues,
|
||||||
|
object values,
|
||||||
|
string expected)
|
||||||
|
{
|
||||||
|
RunTest(
|
||||||
|
pattern,
|
||||||
|
new DispatcherValueCollection(defaults),
|
||||||
|
new DispatcherValueCollection(ambientValues),
|
||||||
|
new DispatcherValueCollection(values),
|
||||||
|
expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunTest(
|
||||||
string pattern,
|
string pattern,
|
||||||
DispatcherValueCollection defaults,
|
DispatcherValueCollection defaults,
|
||||||
DispatcherValueCollection ambientValues,
|
DispatcherValueCollection ambientValues,
|
||||||
|
|
@ -1125,16 +1150,11 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
UrlEncoder encoder = null)
|
UrlEncoder encoder = null)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
encoder = encoder ?? new UrlTestEncoder();
|
var binderFactory = encoder == null ? BinderFactory : new RoutePatternBinderFactory(encoder, new DefaultObjectPoolProvider());
|
||||||
|
var binder = binderFactory.Create(pattern, defaults ?? new DispatcherValueCollection());
|
||||||
var binder = new RoutePatternBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
RoutePattern.Parse(pattern),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues, values);
|
(var acceptedValues, var combinedValues) = binder.GetValues(ambientValues, values);
|
||||||
if (acceptedValues == null)
|
if (acceptedValues == null)
|
||||||
{
|
{
|
||||||
if (expected == null)
|
if (expected == null)
|
||||||
|
|
@ -1180,40 +1200,6 @@ namespace Microsoft.AspNetCore.Dispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunTest(
|
|
||||||
string pattern,
|
|
||||||
object defaults,
|
|
||||||
object ambientValues,
|
|
||||||
object values,
|
|
||||||
string expected)
|
|
||||||
{
|
|
||||||
RunTest(
|
|
||||||
pattern,
|
|
||||||
new DispatcherValueCollection(defaults),
|
|
||||||
new DispatcherValueCollection(ambientValues),
|
|
||||||
new DispatcherValueCollection(values),
|
|
||||||
expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[InlineData(null, null, true)]
|
|
||||||
[InlineData("blog", null, false)]
|
|
||||||
[InlineData(null, "store", false)]
|
|
||||||
[InlineData("Cool", "cool", true)]
|
|
||||||
[InlineData("Co0l", "cool", false)]
|
|
||||||
public void RoutePartsEqualTest(object left, object right, bool expected)
|
|
||||||
{
|
|
||||||
// Arrange & Act & Assert
|
|
||||||
if (expected)
|
|
||||||
{
|
|
||||||
Assert.True(RoutePatternBinder.RoutePartsEqual(left, right));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.False(RoutePatternBinder.RoutePartsEqual(left, right));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PathAndQuery
|
private class PathAndQuery
|
||||||
{
|
{
|
||||||
public PathAndQuery(string uri)
|
public PathAndQuery(string uri)
|
||||||
|
|
@ -8,7 +8,7 @@ using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Dispatcher
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
{
|
{
|
||||||
public class RoutePatternMatcherTests
|
public class RoutePatternMatcherTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TryMatch_Success()
|
public void TryMatch_Success()
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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.Text.Encodings.Web;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Dispatcher
|
||||||
|
{
|
||||||
|
// Not getting too in-depth with the tests here, the core URL generation is already tested elsewhere
|
||||||
|
public class RoutePatternTemplateTest
|
||||||
|
{
|
||||||
|
public RoutePatternTemplateTest()
|
||||||
|
{
|
||||||
|
BinderFactory = new RoutePatternBinderFactory(UrlEncoder.Default, new DefaultObjectPoolProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinderFactory BinderFactory { get; }
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetUrl_WithAllRequiredValues_GeneratesUrl()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var template = new RoutePatternTemplate(BinderFactory.Create("api/products/{id}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url = template.GetUrl(new DispatcherValueCollection(new { id = 17 }));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/api/products/17", url);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetUrl_WithoutAllRequiredValues_GeneratesUrl()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var template = new RoutePatternTemplate(BinderFactory.Create("api/products/{id}"));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url = template.GetUrl(new DispatcherValueCollection(new { name = "billy" }));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetUrl_WithAmbientValues_GeneratesUrl()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var template = new RoutePatternTemplate(BinderFactory.Create("api/products/{id}/{name}"));
|
||||||
|
|
||||||
|
var httpContext = new DefaultHttpContext();
|
||||||
|
httpContext.Features.Set<IDispatcherFeature>(new DispatcherFeature()
|
||||||
|
{
|
||||||
|
Values = new DispatcherValueCollection(new { id = 17 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var url = template.GetUrl(httpContext, new DispatcherValueCollection(new { name = "billy" }));
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal("/api/products/17/billy", url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// 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;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.AspNetCore.Routing.Constraints;
|
using Microsoft.AspNetCore.Routing.Constraints;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -10,18 +9,6 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
||||||
{
|
{
|
||||||
public class DateTimeRouteConstraintTests
|
public class DateTimeRouteConstraintTests
|
||||||
{
|
{
|
||||||
public static IEnumerable<object[]> GetDateTimeObject
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
yield return new object[]
|
|
||||||
{
|
|
||||||
DateTime.Now,
|
|
||||||
true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("12/25/2009", true)]
|
[InlineData("12/25/2009", true)]
|
||||||
[InlineData("25/12/2009 11:45:00 PM", false)]
|
[InlineData("25/12/2009 11:45:00 PM", false)]
|
||||||
|
|
@ -36,9 +23,7 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
||||||
[InlineData("12/25/2009 11:45:00 PM", true)]
|
[InlineData("12/25/2009 11:45:00 PM", true)]
|
||||||
[InlineData("2009-05-12T11:45:00Z", true)]
|
[InlineData("2009-05-12T11:45:00Z", true)]
|
||||||
[InlineData("not-parseable-as-date", false)]
|
[InlineData("not-parseable-as-date", false)]
|
||||||
[InlineData(false, false)]
|
public void DateTimeRouteConstraint_ParsesStrings(string parameterValue, bool expected)
|
||||||
[MemberData(nameof(GetDateTimeObject))]
|
|
||||||
public void DateTimeRouteConstraint(object parameterValue, bool expected)
|
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var constraint = new DateTimeRouteConstraint();
|
var constraint = new DateTimeRouteConstraint();
|
||||||
|
|
@ -49,5 +34,31 @@ namespace Microsoft.AspNetCore.Routing.Tests
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expected, actual);
|
Assert.Equal(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DateTimeRouteConstraint_AcceptsDateTimeObjects_ReturnsTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new DateTimeRouteConstraint();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = ConstraintsTestHelper.TestConstraint(constraint, DateTime.Now);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DateTimeRouteConstraint_IgnoresOtherTypes_ReturnsFalse()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var constraint = new DateTimeRouteConstraint();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = ConstraintsTestHelper.TestConstraint(constraint, false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(actual);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Encodings.Web;
|
using System.Text.Encodings.Web;
|
||||||
using Microsoft.AspNetCore.Dispatcher;
|
using Microsoft.AspNetCore.Dispatcher;
|
||||||
using Microsoft.AspNetCore.Routing.Internal;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
@ -17,7 +16,12 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
{
|
{
|
||||||
public class TemplateBinderTests
|
public class TemplateBinderTests
|
||||||
{
|
{
|
||||||
private readonly IInlineConstraintResolver _inlineConstraintResolver = GetInlineConstraintResolver();
|
public TemplateBinderTests()
|
||||||
|
{
|
||||||
|
BinderFactory = new RoutePatternBinderFactory(new UrlTestEncoder(), new DefaultObjectPoolProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinderFactory BinderFactory { get; }
|
||||||
|
|
||||||
public static TheoryData EmptyAndNullDefaultValues =>
|
public static TheoryData EmptyAndNullDefaultValues =>
|
||||||
new TheoryData<string, RouteValueDictionary, RouteValueDictionary, string>
|
new TheoryData<string, RouteValueDictionary, RouteValueDictionary, string>
|
||||||
|
|
@ -117,12 +121,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var encoder = new UrlTestEncoder();
|
var binder = new TemplateBinder(BinderFactory.Create(template, defaults));
|
||||||
var binder = new TemplateBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
TemplateParser.Parse(template),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var result = binder.GetValues(ambientValues: null, values: values);
|
var result = binder.GetValues(ambientValues: null, values: values);
|
||||||
|
|
@ -267,12 +266,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
string expected)
|
string expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var encoder = new UrlTestEncoder();
|
var binder = new TemplateBinder(BinderFactory.Create(template, defaults));
|
||||||
var binder = new TemplateBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
TemplateParser.Parse(template),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var result = binder.GetValues(ambientValues: ambientValues, values: values);
|
var result = binder.GetValues(ambientValues: ambientValues, values: values);
|
||||||
|
|
@ -697,12 +691,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var template = "{area?}/{controller=Home}/{action=Index}/{id?}";
|
var template = "{area?}/{controller=Home}/{action=Index}/{id?}";
|
||||||
var encoder = new UrlTestEncoder();
|
var binder = new TemplateBinder(BinderFactory.Create(template));
|
||||||
var binder = new TemplateBinder(
|
|
||||||
new UrlTestEncoder(),
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
TemplateParser.Parse(template),
|
|
||||||
defaults: null);
|
|
||||||
var ambientValues = new RouteValueDictionary();
|
var ambientValues = new RouteValueDictionary();
|
||||||
var routeValues = new RouteValueDictionary(new { controller = "Test", action = "Index" });
|
var routeValues = new RouteValueDictionary(new { controller = "Test", action = "Index" });
|
||||||
|
|
||||||
|
|
@ -1119,7 +1109,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private static void RunTest(
|
private void RunTest(
|
||||||
string template,
|
string template,
|
||||||
RouteValueDictionary defaults,
|
RouteValueDictionary defaults,
|
||||||
RouteValueDictionary ambientValues,
|
RouteValueDictionary ambientValues,
|
||||||
|
|
@ -1128,13 +1118,8 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
UrlEncoder encoder = null)
|
UrlEncoder encoder = null)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
encoder = encoder ?? new UrlTestEncoder();
|
var binderFactory = encoder == null ? BinderFactory : new RoutePatternBinderFactory(encoder, new DefaultObjectPoolProvider());
|
||||||
|
var binder = new TemplateBinder(binderFactory.Create(template, defaults ?? new RouteValueDictionary()));
|
||||||
var binder = new TemplateBinder(
|
|
||||||
encoder,
|
|
||||||
new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy()),
|
|
||||||
TemplateParser.Parse(template),
|
|
||||||
defaults);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
var result = binder.GetValues(ambientValues, values);
|
var result = binder.GetValues(ambientValues, values);
|
||||||
|
|
@ -1183,7 +1168,7 @@ namespace Microsoft.AspNetCore.Routing.Template.Tests
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunTest(
|
private void RunTest(
|
||||||
string template,
|
string template,
|
||||||
object defaults,
|
object defaults,
|
||||||
object ambientValues,
|
object ambientValues,
|
||||||
|
|
|
||||||
|
|
@ -243,15 +243,10 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
|
|
||||||
private static TreeRouteBuilder CreateBuilder()
|
private static TreeRouteBuilder CreateBuilder()
|
||||||
{
|
{
|
||||||
var objectPoolProvider = new DefaultObjectPoolProvider();
|
|
||||||
var objectPolicy = new UriBuilderContextPooledObjectPolicy();
|
|
||||||
var objectPool = objectPoolProvider.Create(objectPolicy);
|
|
||||||
|
|
||||||
var constraintResolver = GetInlineConstraintResolver();
|
var constraintResolver = GetInlineConstraintResolver();
|
||||||
var builder = new TreeRouteBuilder(
|
var builder = new TreeRouteBuilder(
|
||||||
NullLoggerFactory.Instance,
|
NullLoggerFactory.Instance,
|
||||||
UrlEncoder.Default,
|
new RoutePatternBinderFactory(UrlEncoder.Default, new DefaultObjectPoolProvider()),
|
||||||
objectPool,
|
|
||||||
constraintResolver);
|
constraintResolver);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,12 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
{
|
{
|
||||||
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
|
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
|
||||||
|
|
||||||
private static UrlEncoder Encoder = UrlTestEncoder.Default;
|
public TreeRouterTest()
|
||||||
private static ObjectPool<UriBuildingContext> Pool = new DefaultObjectPoolProvider().Create(new UriBuilderContextPooledObjectPolicy());
|
{
|
||||||
|
BinderFactory = new RoutePatternBinderFactory(new UrlTestEncoder(), new DefaultObjectPoolProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutePatternBinderFactory BinderFactory { get; }
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("template/5", "template/{parameter:int}")]
|
[InlineData("template/5", "template/{parameter:int}")]
|
||||||
|
|
@ -1988,15 +1992,10 @@ namespace Microsoft.AspNetCore.Routing.Tree
|
||||||
|
|
||||||
private static TreeRouteBuilder CreateBuilder()
|
private static TreeRouteBuilder CreateBuilder()
|
||||||
{
|
{
|
||||||
var objectPoolProvider = new DefaultObjectPoolProvider();
|
|
||||||
var objectPolicy = new UriBuilderContextPooledObjectPolicy();
|
|
||||||
var objectPool = objectPoolProvider.Create<UriBuildingContext>(objectPolicy);
|
|
||||||
|
|
||||||
var constraintResolver = CreateConstraintResolver();
|
var constraintResolver = CreateConstraintResolver();
|
||||||
var builder = new TreeRouteBuilder(
|
var builder = new TreeRouteBuilder(
|
||||||
NullLoggerFactory.Instance,
|
NullLoggerFactory.Instance,
|
||||||
UrlEncoder.Default,
|
new RoutePatternBinderFactory(UrlTestEncoder.Default, new DefaultObjectPoolProvider()),
|
||||||
objectPool,
|
|
||||||
constraintResolver);
|
constraintResolver);
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue