Add IRouteHandler, RouteBase, and IRoutingFeature
Adds IRouterHandler, an abstraction for endpoints in the routing system that can't chain (example: delegates). The idea is that some kinds of routes aren't really friendly to chaining. If you don't support chaining, then accept IRouteHandler and work with that rather than IRouter. There's one implementation of IRouteHandler, RouteHandler. It implements both IRouter and IRouteHandler. Adds RouteBase as a base class for routes based on our template syntax and defaults/constraints/data-tokens. Updated a lot of signatures to be get/set virtual and mutable to facilitate or bigger variety of usage scenarios. Renamed TemplateRoute to just Route, now inherits from RouteBase. Adds IRoutingFeature for middleware scenarios where you don't have access to the route context. Also adds some basic extension methods for accessing route values.
This commit is contained in:
parent
411a59125c
commit
36180ab6d0
|
|
@ -1,31 +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.Threading.Tasks;
|
||||
using Microsoft.AspNet.Routing;
|
||||
|
||||
namespace RoutingSample.Web
|
||||
{
|
||||
public class DelegateRouteEndpoint : IRouter
|
||||
{
|
||||
public delegate Task RoutedDelegate(RouteContext context);
|
||||
|
||||
private readonly RoutedDelegate _appFunc;
|
||||
|
||||
public DelegateRouteEndpoint(RoutedDelegate appFunc)
|
||||
{
|
||||
_appFunc = appFunc;
|
||||
}
|
||||
|
||||
public async Task RouteAsync(RouteContext context)
|
||||
{
|
||||
await _appFunc(context);
|
||||
context.IsHandled = true;
|
||||
}
|
||||
|
||||
public VirtualPathData GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +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 System.Linq;
|
||||
|
||||
namespace RoutingSample.Web
|
||||
{
|
||||
public static class DictionaryExtensions
|
||||
{
|
||||
public static string Print(this IDictionary<string, object> routeValues)
|
||||
{
|
||||
var values = routeValues.Select(kvp => kvp.Key + ":" + kvp.Value.ToString());
|
||||
|
||||
return string.Join(" ", values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,12 +7,12 @@ using Microsoft.AspNet.Routing;
|
|||
|
||||
namespace RoutingSample.Web
|
||||
{
|
||||
internal class PrefixRoute : IRouter
|
||||
public class PrefixRoute : IRouter
|
||||
{
|
||||
private readonly IRouter _target;
|
||||
private readonly IRouteHandler _target;
|
||||
private readonly string _prefix;
|
||||
|
||||
public PrefixRoute(IRouter target, string prefix)
|
||||
public PrefixRoute(IRouteHandler target, string prefix)
|
||||
{
|
||||
_target = target;
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ namespace RoutingSample.Web
|
|||
_prefix = prefix;
|
||||
}
|
||||
|
||||
public async Task RouteAsync(RouteContext context)
|
||||
public Task RouteAsync(RouteContext context)
|
||||
{
|
||||
var requestPath = context.HttpContext.Request.Path.Value ?? string.Empty;
|
||||
if (requestPath.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -44,12 +44,14 @@ namespace RoutingSample.Web
|
|||
var lastCharacter = requestPath[_prefix.Length];
|
||||
if (lastCharacter != '/' && lastCharacter != '#' && lastCharacter != '?')
|
||||
{
|
||||
return;
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
await _target.RouteAsync(context);
|
||||
context.Handler = _target.GetRequestHandler(context.HttpContext, context.RouteData);
|
||||
}
|
||||
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public VirtualPathData GetVirtualPath(VirtualPathContext context)
|
||||
|
|
|
|||
|
|
@ -1,34 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace RoutingSample.Web
|
||||
{
|
||||
public static class RouteBuilderExtensions
|
||||
{
|
||||
public static IRouteBuilder AddPrefixRoute(this IRouteBuilder routeBuilder,
|
||||
string prefix)
|
||||
{
|
||||
if (routeBuilder.DefaultHandler == null)
|
||||
{
|
||||
throw new InvalidOperationException("DefaultHandler must be set.");
|
||||
}
|
||||
|
||||
if (routeBuilder.ServiceProvider == null)
|
||||
{
|
||||
throw new InvalidOperationException("ServiceProvider must be set.");
|
||||
}
|
||||
|
||||
return AddPrefixRoute(routeBuilder, prefix, routeBuilder.DefaultHandler);
|
||||
}
|
||||
|
||||
public static IRouteBuilder AddPrefixRoute(this IRouteBuilder routeBuilder,
|
||||
string prefix,
|
||||
IRouter handler)
|
||||
public static IRouteBuilder AddPrefixRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string prefix,
|
||||
IRouteHandler handler)
|
||||
{
|
||||
routeBuilder.Routes.Add(new PrefixRoute(handler, prefix));
|
||||
return routeBuilder;
|
||||
|
|
@ -45,7 +28,7 @@ namespace RoutingSample.Web
|
|||
|
||||
var constraintResolver = routeBuilder.ServiceProvider.GetService<IInlineConstraintResolver>();
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
target: routeBuilder.DefaultHandler,
|
||||
routeTemplate: routeTemplate,
|
||||
defaults: defaultsDictionary,
|
||||
|
|
|
|||
|
|
@ -1,12 +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.
|
||||
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace RoutingSample.Web
|
||||
|
|
@ -20,46 +17,20 @@ namespace RoutingSample.Web
|
|||
|
||||
public void Configure(IApplicationBuilder builder)
|
||||
{
|
||||
var endpoint1 = new DelegateRouteEndpoint(async (context) =>
|
||||
await context
|
||||
.HttpContext
|
||||
.Response
|
||||
.WriteAsync(
|
||||
"match1, route values -" + context.RouteData.Values.Print()));
|
||||
var endpoint1 = new RouteHandler((c) =>
|
||||
{
|
||||
return c.Response.WriteAsync($"match1, route values - {string.Join(", ", c.GetRouteData().Values)}");
|
||||
});
|
||||
|
||||
var endpoint2 = new DelegateRouteEndpoint(async (context) =>
|
||||
await context
|
||||
.HttpContext
|
||||
.Response
|
||||
.WriteAsync("Hello, World!"));
|
||||
var endpoint2 = new RouteHandler((c) => c.Response.WriteAsync("Hello, World!"));
|
||||
|
||||
var routeBuilder = new RouteBuilder();
|
||||
routeBuilder.DefaultHandler = endpoint1;
|
||||
routeBuilder.ServiceProvider = builder.ApplicationServices;
|
||||
|
||||
routeBuilder.AddPrefixRoute("api/store");
|
||||
|
||||
routeBuilder.MapRoute("defaultRoute",
|
||||
"api/constraint/{controller}",
|
||||
null,
|
||||
new { controller = "my.*" });
|
||||
routeBuilder.MapRoute("regexStringRoute",
|
||||
"api/rconstraint/{controller}",
|
||||
new { foo = "Bar" },
|
||||
new { controller = new RegexRouteConstraint("^(my.*)$") });
|
||||
routeBuilder.MapRoute("regexRoute",
|
||||
"api/r2constraint/{controller}",
|
||||
new { foo = "Bar2" },
|
||||
new
|
||||
{
|
||||
controller = new RegexRouteConstraint(
|
||||
new Regex("^(my.*)$", RegexOptions.None, TimeSpan.FromSeconds(10)))
|
||||
});
|
||||
|
||||
routeBuilder.MapRoute("parameterConstraintRoute",
|
||||
"api/{controller}/{*extra}",
|
||||
new { controller = "Store" });
|
||||
var routeBuilder = new RouteBuilder()
|
||||
{
|
||||
DefaultHandler = endpoint1,
|
||||
ServiceProvider = builder.ApplicationServices,
|
||||
};
|
||||
|
||||
routeBuilder.AddPrefixRoute("api/store", endpoint1);
|
||||
routeBuilder.AddPrefixRoute("hello/world", endpoint2);
|
||||
|
||||
routeBuilder.MapLocaleRoute("en-US", "store/US/{action}", new { controller = "Store" });
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
// 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.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a contract for a handler of a route.
|
||||
/// </summary>
|
||||
public interface IRouteHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a <see cref="RequestDelegate"/> to handle the request, based on the provided
|
||||
/// <paramref name="routeData"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="routeData">The <see cref="RouteData"/> associated with the current routing match.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="RequestDelegate"/>, or <c>null</c> if the handler cannot handle this request.
|
||||
/// </returns>
|
||||
RequestDelegate GetRequestHandler(HttpContext httpContext, RouteData routeData);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// A feature interface for routing functionality.
|
||||
/// </summary>
|
||||
public interface IRoutingFeature
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Routing.RouteData"/> associated with the current request.
|
||||
/// </summary>
|
||||
RouteData RouteData { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -6,10 +6,17 @@ using Microsoft.AspNet.Http;
|
|||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// A context object for <see cref="IRouter.RouteAsync(RouteContext)"/>.
|
||||
/// </summary>
|
||||
public class RouteContext
|
||||
{
|
||||
private RouteData _routeData;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RouteContext"/> for the provided <paramref name="httpContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="Http.HttpContext"/> associated with the current request.</param>
|
||||
public RouteContext(HttpContext httpContext)
|
||||
{
|
||||
HttpContext = httpContext;
|
||||
|
|
@ -17,10 +24,20 @@ namespace Microsoft.AspNet.Routing
|
|||
RouteData = new RouteData();
|
||||
}
|
||||
|
||||
public HttpContext HttpContext { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the handler for the request. An <see cref="IRouter"/> should set <see cref="Handler"/>
|
||||
/// when it matches.
|
||||
/// </summary>
|
||||
public RequestDelegate Handler { get; set; }
|
||||
|
||||
public bool IsHandled { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Http.HttpContext"/> associated with the current request.
|
||||
/// </summary>
|
||||
public HttpContext HttpContext { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Routing.RouteData"/> associated with the current context.
|
||||
/// </summary>
|
||||
public RouteData RouteData
|
||||
{
|
||||
get
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Routing
|
|||
/// </summary>
|
||||
public class RouteData
|
||||
{
|
||||
private Dictionary<string, object> _dataTokens;
|
||||
private RouteValueDictionary _dataTokens;
|
||||
private RouteValueDictionary _values;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Routing
|
|||
// Perf: Avoid allocating DataTokens and RouteValues unless we need to make a copy.
|
||||
if (other._dataTokens != null)
|
||||
{
|
||||
_dataTokens = new Dictionary<string, object>(other._dataTokens, StringComparer.OrdinalIgnoreCase);
|
||||
_dataTokens = new RouteValueDictionary(other._dataTokens);
|
||||
}
|
||||
|
||||
if (other._values != null)
|
||||
|
|
@ -51,13 +51,13 @@ namespace Microsoft.AspNet.Routing
|
|||
/// <summary>
|
||||
/// Gets the data tokens produced by routes on the current routing path.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> DataTokens
|
||||
public RouteValueDictionary DataTokens
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dataTokens == null)
|
||||
{
|
||||
_dataTokens = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
||||
_dataTokens = new RouteValueDictionary();
|
||||
}
|
||||
|
||||
return _dataTokens;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
// 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.AspNet.Http;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for <see cref="HttpContext"/> related to routing.
|
||||
/// </summary>
|
||||
public static class RoutingHttpContextExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RouteData"/> associated with the provided <paramref name="httpContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <returns>The <see cref="RouteData"/>, or null.</returns>
|
||||
public static RouteData GetRouteData(this HttpContext httpContext)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
var routingFeature = httpContext.Features[typeof(IRoutingFeature)] as IRoutingFeature;
|
||||
return routingFeature?.RouteData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a route value from <see cref="RouteData.Values"/> associated with the provided
|
||||
/// <paramref name="httpContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="httpContext">The <see cref="HttpContext"/> associated with the current request.</param>
|
||||
/// <param name="key">The key of the route value.</param>
|
||||
/// <returns>The corresponding route value, or null.</returns>
|
||||
public static object GetRouteValue(this HttpContext httpContext, string key)
|
||||
{
|
||||
if (httpContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(httpContext));
|
||||
}
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(key));
|
||||
}
|
||||
|
||||
var routingFeature = httpContext.Features[typeof(IRoutingFeature)] as IRoutingFeature;
|
||||
return routingFeature?.RouteData.Values[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -59,8 +59,8 @@ namespace Microsoft.AspNet.Routing
|
|||
public string RouteName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of new values provided for virtual path generation.
|
||||
/// Gets or sets the set of new values provided for virtual path generation.
|
||||
/// </summary>
|
||||
public RouteValueDictionary Values { get; }
|
||||
public RouteValueDictionary Values { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"dotnet5.4": {
|
||||
"dependencies": {
|
||||
"System.Collections.Concurrent": "4.0.11-*",
|
||||
"System.Threading.Tasks": "4.0.11-*",
|
||||
"System.Reflection.Extensions": "4.0.1-*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,20 @@ using System.Collections.Generic;
|
|||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a contract for a route builder in an application. A route builder specifies the routes for an application.
|
||||
/// Defines a contract for a route builder in an application. A route builder specifies the routes for
|
||||
/// an application.
|
||||
/// </summary>
|
||||
public interface IRouteBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the default <see cref="IRouter"/> that is used if an <see cref="IRouter"/> is added to the list of routes but does not specify its own.
|
||||
/// Gets or sets the default <see cref="IRouter"/> that is used as a handler if an <see cref="IRouter"/>
|
||||
/// is added to the list of routes but does not specify its own.
|
||||
/// </summary>
|
||||
IRouter DefaultHandler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sets the <see cref="IServiceProvider"/> used to resolve services for routes.
|
||||
/// </summary>
|
||||
IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Internal
|
||||
{
|
||||
public static class TaskCache
|
||||
{
|
||||
#if DOTNET5_4
|
||||
public static readonly Task CompletedTask = Task.CompletedTask;
|
||||
#else
|
||||
public static readonly Task CompletedTask = Task.FromResult(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// 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.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class Route : RouteBase
|
||||
{
|
||||
private readonly IRouter _target;
|
||||
|
||||
public Route(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: this(
|
||||
target,
|
||||
routeTemplate,
|
||||
defaults: null,
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: inlineConstraintResolver)
|
||||
{
|
||||
}
|
||||
|
||||
public Route(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
RouteValueDictionary defaults,
|
||||
IDictionary<string, object> constraints,
|
||||
RouteValueDictionary dataTokens,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: this(target, null, routeTemplate, defaults, constraints, dataTokens, inlineConstraintResolver)
|
||||
{
|
||||
}
|
||||
|
||||
public Route(
|
||||
IRouter target,
|
||||
string routeName,
|
||||
string routeTemplate,
|
||||
RouteValueDictionary defaults,
|
||||
IDictionary<string, object> constraints,
|
||||
RouteValueDictionary dataTokens,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: base(
|
||||
routeTemplate,
|
||||
routeName,
|
||||
inlineConstraintResolver,
|
||||
defaults,
|
||||
constraints,
|
||||
dataTokens)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
}
|
||||
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public string RouteTemplate => ParsedTemplate.TemplateText;
|
||||
|
||||
protected override Task OnRouteMatched(RouteContext context)
|
||||
{
|
||||
context.RouteData.Routers.Add(_target);
|
||||
return _target.RouteAsync(context);
|
||||
}
|
||||
|
||||
protected override VirtualPathData OnVirtualPathGenerated(VirtualPathContext context)
|
||||
{
|
||||
return _target.GetVirtualPath(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,118 +1,75 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// 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.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Internal;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class TemplateRoute : INamedRouter
|
||||
public abstract class RouteBase : IRouter, INamedRouter
|
||||
{
|
||||
private readonly IReadOnlyDictionary<string, IRouteConstraint> _constraints;
|
||||
private readonly IReadOnlyDictionary<string, object> _dataTokens;
|
||||
private readonly IReadOnlyDictionary<string, object> _defaults;
|
||||
private readonly IRouter _target;
|
||||
private readonly RouteTemplate _parsedTemplate;
|
||||
private readonly string _routeTemplate;
|
||||
private readonly TemplateMatcher _matcher;
|
||||
private readonly TemplateBinder _binder;
|
||||
private TemplateMatcher _matcher;
|
||||
private TemplateBinder _binder;
|
||||
private ILogger _logger;
|
||||
private ILogger _constraintLogger;
|
||||
|
||||
public TemplateRoute(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: this(
|
||||
target,
|
||||
routeTemplate,
|
||||
defaults: null,
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: inlineConstraintResolver)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(
|
||||
IRouter target,
|
||||
string routeTemplate,
|
||||
IDictionary<string, object> defaults,
|
||||
public RouteBase(
|
||||
string template,
|
||||
string name,
|
||||
IInlineConstraintResolver constraintResolver,
|
||||
RouteValueDictionary defaults,
|
||||
IDictionary<string, object> constraints,
|
||||
IDictionary<string, object> dataTokens,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
: this(target, null, routeTemplate, defaults, constraints, dataTokens, inlineConstraintResolver)
|
||||
RouteValueDictionary dataTokens)
|
||||
{
|
||||
}
|
||||
|
||||
public TemplateRoute(
|
||||
IRouter target,
|
||||
string routeName,
|
||||
string routeTemplate,
|
||||
IDictionary<string, object> defaults,
|
||||
IDictionary<string, object> constraints,
|
||||
IDictionary<string, object> dataTokens,
|
||||
IInlineConstraintResolver inlineConstraintResolver)
|
||||
{
|
||||
if (target == null)
|
||||
if (constraintResolver == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
throw new ArgumentNullException(nameof(constraintResolver));
|
||||
}
|
||||
|
||||
_target = target;
|
||||
_routeTemplate = routeTemplate ?? string.Empty;
|
||||
Name = routeName;
|
||||
|
||||
_dataTokens = dataTokens == null ? new RouteValueDictionary() : new RouteValueDictionary(dataTokens);
|
||||
template = template ?? string.Empty;
|
||||
Name = name;
|
||||
ConstraintResolver = constraintResolver;
|
||||
DataTokens = dataTokens ?? new RouteValueDictionary();
|
||||
|
||||
// Data we parse from the template will be used to fill in the rest of the constraints or
|
||||
// defaults. The parser will throw for invalid routes.
|
||||
_parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
||||
ParsedTemplate = TemplateParser.Parse(template);
|
||||
|
||||
_constraints = GetConstraints(inlineConstraintResolver, RouteTemplate, _parsedTemplate, constraints);
|
||||
_defaults = GetDefaults(_parsedTemplate, defaults);
|
||||
|
||||
_matcher = new TemplateMatcher(_parsedTemplate, Defaults);
|
||||
_binder = new TemplateBinder(_parsedTemplate, Defaults);
|
||||
Constraints = GetConstraints(constraintResolver, ParsedTemplate, constraints);
|
||||
Defaults = GetDefaults(ParsedTemplate, defaults);
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
public virtual IDictionary<string, IRouteConstraint> Constraints { get; protected set; }
|
||||
|
||||
public IReadOnlyDictionary<string, object> Defaults
|
||||
{
|
||||
get { return _defaults; }
|
||||
}
|
||||
protected virtual IInlineConstraintResolver ConstraintResolver { get; set; }
|
||||
|
||||
public IReadOnlyDictionary<string, object> DataTokens
|
||||
{
|
||||
get { return _dataTokens; }
|
||||
}
|
||||
public virtual RouteValueDictionary DataTokens { get; protected set; }
|
||||
|
||||
public RouteTemplate ParsedTemplate
|
||||
{
|
||||
get { return _parsedTemplate; }
|
||||
}
|
||||
public virtual RouteValueDictionary Defaults { get; protected set; }
|
||||
|
||||
public string RouteTemplate
|
||||
{
|
||||
get { return _routeTemplate; }
|
||||
}
|
||||
public virtual string Name { get; protected set; }
|
||||
|
||||
public IReadOnlyDictionary<string, IRouteConstraint> Constraints
|
||||
{
|
||||
get { return _constraints; }
|
||||
}
|
||||
public virtual RouteTemplate ParsedTemplate { get; protected set; }
|
||||
|
||||
public async virtual Task RouteAsync(RouteContext context)
|
||||
protected abstract Task OnRouteMatched(RouteContext context);
|
||||
|
||||
protected abstract VirtualPathData OnVirtualPathGenerated(VirtualPathContext context);
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Task RouteAsync(RouteContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
EnsureMatcher();
|
||||
EnsureLoggers(context.HttpContext);
|
||||
|
||||
var requestPath = context.HttpContext.Request.Path;
|
||||
|
|
@ -121,57 +78,46 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
if (values == null)
|
||||
{
|
||||
// If we got back a null value set, that means the URI did not match
|
||||
return;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
var oldRouteData = context.RouteData;
|
||||
|
||||
var newRouteData = new RouteData(oldRouteData);
|
||||
|
||||
// Perf: Avoid accessing data tokens if you don't need to write to it, these dictionaries are all
|
||||
// Perf: Avoid accessing dictionaries if you don't need to write to them, these dictionaries are all
|
||||
// created lazily.
|
||||
if (_dataTokens.Count > 0)
|
||||
if (DataTokens.Count > 0)
|
||||
{
|
||||
MergeValues(newRouteData.DataTokens, _dataTokens);
|
||||
MergeValues(context.RouteData.DataTokens, DataTokens);
|
||||
}
|
||||
|
||||
newRouteData.Routers.Add(_target);
|
||||
MergeValues(newRouteData.Values, values);
|
||||
if (values.Count > 0)
|
||||
{
|
||||
MergeValues(context.RouteData.Values, values);
|
||||
}
|
||||
|
||||
if (!RouteConstraintMatcher.Match(
|
||||
Constraints,
|
||||
newRouteData.Values,
|
||||
context.RouteData.Values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest,
|
||||
_constraintLogger))
|
||||
{
|
||||
return;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
|
||||
_logger.LogDebug(
|
||||
"Request successfully matched the route with name '{RouteName}' and template '{RouteTemplate}'.",
|
||||
Name,
|
||||
RouteTemplate);
|
||||
ParsedTemplate.TemplateText);
|
||||
|
||||
try
|
||||
{
|
||||
context.RouteData = newRouteData;
|
||||
|
||||
await _target.RouteAsync(context);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore the original values to prevent polluting the route data.
|
||||
if (!context.IsHandled)
|
||||
{
|
||||
context.RouteData = oldRouteData;
|
||||
}
|
||||
}
|
||||
return OnRouteMatched(context);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual VirtualPathData GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
EnsureBinder();
|
||||
EnsureLoggers(context.HttpContext);
|
||||
|
||||
var values = _binder.GetValues(context.AmbientValues, context.Values);
|
||||
if (values == null)
|
||||
{
|
||||
|
|
@ -179,7 +125,6 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return null;
|
||||
}
|
||||
|
||||
EnsureLoggers(context.HttpContext);
|
||||
if (!RouteConstraintMatcher.Match(
|
||||
Constraints,
|
||||
values.CombinedValues,
|
||||
|
|
@ -191,7 +136,9 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return null;
|
||||
}
|
||||
|
||||
var pathData = _target.GetVirtualPath(context);
|
||||
context.Values = values.CombinedValues;
|
||||
|
||||
var pathData = OnVirtualPathGenerated(context);
|
||||
if (pathData != null)
|
||||
{
|
||||
// If the target generates a value then that can short circuit.
|
||||
|
|
@ -202,13 +149,13 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
// to see if the values were validated.
|
||||
|
||||
// When we still cannot produce a value, this should return null.
|
||||
var tempPath = _binder.BindValues(values.AcceptedValues);
|
||||
if (tempPath == null)
|
||||
var virtualPath = _binder.BindValues(values.AcceptedValues);
|
||||
if (virtualPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
pathData = new VirtualPathData(this, tempPath);
|
||||
pathData = new VirtualPathData(this, virtualPath);
|
||||
if (DataTokens != null)
|
||||
{
|
||||
foreach (var dataToken in DataTokens)
|
||||
|
|
@ -220,13 +167,12 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return pathData;
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IRouteConstraint> GetConstraints(
|
||||
protected static IDictionary<string, IRouteConstraint> GetConstraints(
|
||||
IInlineConstraintResolver inlineConstraintResolver,
|
||||
string template,
|
||||
RouteTemplate parsedTemplate,
|
||||
IDictionary<string, object> constraints)
|
||||
{
|
||||
var constraintBuilder = new RouteConstraintBuilder(inlineConstraintResolver, template);
|
||||
var constraintBuilder = new RouteConstraintBuilder(inlineConstraintResolver, parsedTemplate.TemplateText);
|
||||
|
||||
if (constraints != null)
|
||||
{
|
||||
|
|
@ -252,12 +198,10 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return constraintBuilder.Build();
|
||||
}
|
||||
|
||||
private static RouteValueDictionary GetDefaults(
|
||||
protected static RouteValueDictionary GetDefaults(
|
||||
RouteTemplate parsedTemplate,
|
||||
IDictionary<string, object> defaults)
|
||||
RouteValueDictionary defaults)
|
||||
{
|
||||
// Do not use RouteValueDictionary.Empty for defaults, it might be modified inside
|
||||
// UpdateInlineDefaultValuesAndConstraints()
|
||||
var result = defaults == null ? new RouteValueDictionary() : new RouteValueDictionary(defaults);
|
||||
|
||||
foreach (var parameter in parsedTemplate.Parameters)
|
||||
|
|
@ -293,17 +237,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
}
|
||||
}
|
||||
|
||||
// Needed because IDictionary<> is not an IReadOnlyDictionary<>
|
||||
private static void MergeValues(
|
||||
IDictionary<string, object> destination,
|
||||
IReadOnlyDictionary<string, object> values)
|
||||
private void EnsureBinder()
|
||||
{
|
||||
foreach (var kvp in values)
|
||||
if (_binder == null)
|
||||
{
|
||||
// This will replace the original value for the specified key.
|
||||
// Values from the matched route will take preference over previous
|
||||
// data in the route context.
|
||||
destination[kvp.Key] = kvp.Value;
|
||||
_binder = new TemplateBinder(ParsedTemplate, Defaults);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -312,14 +250,22 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
if (_logger == null)
|
||||
{
|
||||
var factory = context.RequestServices.GetRequiredService<ILoggerFactory>();
|
||||
_logger = factory.CreateLogger<TemplateRoute>();
|
||||
_logger = factory.CreateLogger(typeof(RouteBase).FullName);
|
||||
_constraintLogger = factory.CreateLogger(typeof(RouteConstraintMatcher).FullName);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureMatcher()
|
||||
{
|
||||
if (_matcher == null)
|
||||
{
|
||||
_matcher = new TemplateMatcher(ParsedTemplate, Defaults);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _routeTemplate;
|
||||
return ParsedTemplate.TemplateText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,7 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
public IServiceProvider ServiceProvider { get; set; }
|
||||
|
||||
public IList<IRouter> Routes
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
public IList<IRouter> Routes { get; }
|
||||
|
||||
public IRouter Build()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ namespace Microsoft.AspNet.Builder
|
|||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapRoute(this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template)
|
||||
public static IRouteBuilder MapRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template)
|
||||
{
|
||||
MapRoute(routeBuilder, name, template, defaults: null);
|
||||
return routeBuilder;
|
||||
|
|
@ -35,50 +36,73 @@ namespace Microsoft.AspNet.Builder
|
|||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">An object that contains default values for route parameters. The object's properties represent the names and values of the default values.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the names
|
||||
/// and values of the default values.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapRoute(this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults)
|
||||
public static IRouteBuilder MapRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults)
|
||||
{
|
||||
return MapRoute(routeBuilder, name, template, defaults, constraints: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the specified name, template, default values, and constraints.
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the specified name, template, default values, and
|
||||
/// constraints.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">An object that contains default values for route parameters. The object's properties represent the names and values of the default values.</param>
|
||||
/// <param name="constraints">An object that contains constraints for the route. The object's properties represent the names and values of the constraints.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the names
|
||||
/// and values of the default values.
|
||||
/// </param>
|
||||
/// <param name="constraints">
|
||||
/// An object that contains constraints for the route. The object's properties represent the names and values
|
||||
/// of the constraints.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapRoute(this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints)
|
||||
public static IRouteBuilder MapRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints)
|
||||
{
|
||||
return MapRoute(routeBuilder, name, template, defaults, constraints, dataTokens: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the specified name, template, default values, and data tokens.
|
||||
/// Adds a route to the <see cref="IRouteBuilder"/> with the specified name, template, default values, and
|
||||
/// data tokens.
|
||||
/// </summary>
|
||||
/// <param name="routeBuilder">The <see cref="IRouteBuilder"/> to add the route to.</param>
|
||||
/// <param name="name">The name of the route.</param>
|
||||
/// <param name="template">The URL pattern of the route.</param>
|
||||
/// <param name="defaults">An object that contains default values for route parameters. The object's properties represent the names and values of the default values.</param>
|
||||
/// <param name="constraints">An object that contains constraints for the route. The object's properties represent the names and values of the constraints.</param>
|
||||
/// <param name="dataTokens">An object that contains data tokens for the route. The object's properties represent the names and values of the data tokens.</param>
|
||||
/// <param name="defaults">
|
||||
/// An object that contains default values for route parameters. The object's properties represent the names
|
||||
/// and values of the default values.
|
||||
/// </param>
|
||||
/// <param name="constraints">
|
||||
/// An object that contains constraints for the route. The object's properties represent the names and values
|
||||
/// of the constraints.
|
||||
/// </param>
|
||||
/// <param name="dataTokens">
|
||||
/// An object that contains data tokens for the route. The object's properties represent the names and values
|
||||
/// of the data tokens.
|
||||
/// </param>
|
||||
/// <returns>A reference to this instance after the operation has completed.</returns>
|
||||
public static IRouteBuilder MapRoute(this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints,
|
||||
object dataTokens)
|
||||
public static IRouteBuilder MapRoute(
|
||||
this IRouteBuilder routeBuilder,
|
||||
string name,
|
||||
string template,
|
||||
object defaults,
|
||||
object constraints,
|
||||
object dataTokens)
|
||||
{
|
||||
if (routeBuilder.DefaultHandler == null)
|
||||
{
|
||||
|
|
@ -88,26 +112,17 @@ namespace Microsoft.AspNet.Builder
|
|||
var inlineConstraintResolver = routeBuilder
|
||||
.ServiceProvider
|
||||
.GetRequiredService<IInlineConstraintResolver>();
|
||||
routeBuilder.Routes.Add(new TemplateRoute(routeBuilder.DefaultHandler,
|
||||
name,
|
||||
template,
|
||||
ObjectToDictionary(defaults),
|
||||
ObjectToDictionary(constraints),
|
||||
ObjectToDictionary(dataTokens),
|
||||
inlineConstraintResolver));
|
||||
|
||||
routeBuilder.Routes.Add(new Route(
|
||||
routeBuilder.DefaultHandler,
|
||||
name,
|
||||
template,
|
||||
new RouteValueDictionary(defaults),
|
||||
new RouteValueDictionary(constraints),
|
||||
new RouteValueDictionary(dataTokens),
|
||||
inlineConstraintResolver));
|
||||
|
||||
return routeBuilder;
|
||||
}
|
||||
|
||||
private static IDictionary<string, object> ObjectToDictionary(object value)
|
||||
{
|
||||
var dictionary = value as IDictionary<string, object>;
|
||||
if (dictionary != null)
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
return new RouteValueDictionary(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -69,14 +68,15 @@ namespace Microsoft.AspNet.Routing
|
|||
context.RouteData = newRouteData;
|
||||
|
||||
await route.RouteAsync(context);
|
||||
if (context.IsHandled)
|
||||
|
||||
if (context.Handler != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!context.IsHandled)
|
||||
if (context.Handler == null)
|
||||
{
|
||||
context.RouteData = oldRouteData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ namespace Microsoft.AspNet.Routing
|
|||
/// <summary>
|
||||
/// Builds a mapping of constraints.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="IReadOnlyDictionary{string, IRouteConstraint}"/> of the constraints.</returns>
|
||||
public IReadOnlyDictionary<string, IRouteConstraint> Build()
|
||||
/// <returns>An <see cref="IDictionary{string, IRouteConstraint}"/> of the constraints.</returns>
|
||||
public IDictionary<string, IRouteConstraint> Build()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var kvp in _constraints)
|
||||
|
|
|
|||
|
|
@ -10,12 +10,13 @@ namespace Microsoft.AspNet.Routing
|
|||
{
|
||||
public static class RouteConstraintMatcher
|
||||
{
|
||||
public static bool Match(IReadOnlyDictionary<string, IRouteConstraint> constraints,
|
||||
IDictionary<string, object> routeValues,
|
||||
HttpContext httpContext,
|
||||
IRouter route,
|
||||
RouteDirection routeDirection,
|
||||
ILogger logger)
|
||||
public static bool Match(
|
||||
IDictionary<string, IRouteConstraint> constraints,
|
||||
IDictionary<string, object> routeValues,
|
||||
HttpContext httpContext,
|
||||
IRouter route,
|
||||
RouteDirection routeDirection,
|
||||
ILogger logger)
|
||||
{
|
||||
if (routeValues == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class RouteHandler : IRouteHandler, IRouter
|
||||
{
|
||||
private readonly RequestDelegate _requestDelegate;
|
||||
|
||||
public RouteHandler(RequestDelegate requestDelegate)
|
||||
{
|
||||
_requestDelegate = requestDelegate;
|
||||
}
|
||||
|
||||
public RequestDelegate GetRequestHandler(HttpContext httpContext, RouteData routeData)
|
||||
{
|
||||
return _requestDelegate;
|
||||
}
|
||||
|
||||
public VirtualPathData GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
// Nothing to do.
|
||||
return null;
|
||||
}
|
||||
|
||||
public Task RouteAsync(RouteContext context)
|
||||
{
|
||||
context.Handler = _requestDelegate;
|
||||
return TaskCache.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,6 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.AspNet.Routing.Logging.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Builder
|
||||
|
|
@ -35,12 +33,26 @@ namespace Microsoft.AspNet.Builder
|
|||
|
||||
await _router.RouteAsync(context);
|
||||
|
||||
if (!context.IsHandled)
|
||||
if (context.Handler == null)
|
||||
{
|
||||
_logger.LogDebug("Request did not match any routes.");
|
||||
|
||||
httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
|
||||
{
|
||||
RouteData = context.RouteData,
|
||||
};
|
||||
|
||||
await _next.Invoke(httpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
await context.Handler(context.HttpContext);
|
||||
}
|
||||
}
|
||||
|
||||
private class RoutingFeature : IRoutingFeature
|
||||
{
|
||||
public RouteData RouteData { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,47 +13,48 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
public class TreeRouteLinkGenerationEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="TemplateBinder"/>.
|
||||
/// Gets or sets the <see cref="TemplateBinder"/>.
|
||||
/// </summary>
|
||||
public TemplateBinder Binder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The route constraints.
|
||||
/// Gets or sets the route constraints.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
public IDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The route defaults.
|
||||
/// Gets or sets the route defaults.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, object> Defaults { get; set; }
|
||||
public IDictionary<string, object> Defaults { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The order of the template.
|
||||
/// Gets or sets the order of the template.
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The precedence of the template for link generation. Greater number means higher precedence.
|
||||
/// Gets or sets the precedence of the template for link generation. A greater value of
|
||||
/// <see cref="GenerationPrecedence"/> means that an entry is considered first.
|
||||
/// </summary>
|
||||
public decimal GenerationPrecedence { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the route.
|
||||
/// Gets or sets the name of the route.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The route group.
|
||||
/// Gets or sets the route group.
|
||||
/// </summary>
|
||||
public string RouteGroup { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The set of values that must be present for link genration.
|
||||
/// Gets or sets the set of values that must be present for link genration.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> RequiredLinkValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Template"/>.
|
||||
/// Gets or sets the <see cref="Template"/>.
|
||||
/// </summary>
|
||||
public RouteTemplate Template { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,6 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
/// <summary>
|
||||
/// The route constraints.
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
public IDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,20 +199,19 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
try
|
||||
{
|
||||
await match.Entry.Target.RouteAsync(context);
|
||||
if (context.Handler != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!context.IsHandled)
|
||||
if (context.Handler == null)
|
||||
{
|
||||
// Restore the original values to prevent polluting the route data.
|
||||
context.RouteData = oldRouteData;
|
||||
}
|
||||
}
|
||||
|
||||
if (context.IsHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.AspNet.Routing.Template;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -18,6 +17,8 @@ namespace Microsoft.AspNet.Routing
|
|||
{
|
||||
public class RouteCollectionTest
|
||||
{
|
||||
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
|
||||
|
||||
[Theory]
|
||||
[InlineData(@"Home/Index/23", "/home/index/23", true, false)]
|
||||
[InlineData(@"Home/Index/23", "/Home/Index/23", false, false)]
|
||||
|
|
@ -143,7 +144,7 @@ namespace Microsoft.AspNet.Routing
|
|||
// Assert
|
||||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(0));
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
|
||||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||||
Assert.Same(route1.Object, context.RouteData.Routers[0]);
|
||||
|
|
@ -169,7 +170,7 @@ namespace Microsoft.AspNet.Routing
|
|||
// Assert
|
||||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
|
||||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||||
Assert.Same(route2.Object, context.RouteData.Routers[0]);
|
||||
|
|
@ -194,7 +195,7 @@ namespace Microsoft.AspNet.Routing
|
|||
// Assert
|
||||
route1.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||||
route2.Verify(e => e.RouteAsync(It.IsAny<RouteContext>()), Times.Exactly(1));
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
|
||||
Assert.Empty(context.RouteData.Routers);
|
||||
}
|
||||
|
|
@ -485,14 +486,14 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
target
|
||||
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) => c.IsHandled = accept)
|
||||
.Callback<RouteContext>((c) => c.Handler = accept ? NullHandler : null)
|
||||
.Returns(Task.FromResult<object>(null))
|
||||
.Verifiable();
|
||||
|
||||
return target.Object;
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateTemplateRoute(
|
||||
private static Route CreateTemplateRoute(
|
||||
string template,
|
||||
string routerName = null,
|
||||
RouteValueDictionary dataTokens = null)
|
||||
|
|
@ -504,7 +505,7 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
var resolverMock = new Mock<IInlineConstraintResolver>();
|
||||
|
||||
return new TemplateRoute(
|
||||
return new Route(
|
||||
target.Object,
|
||||
routerName,
|
||||
template,
|
||||
|
|
@ -610,7 +611,7 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
target
|
||||
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) => c.IsHandled = accept)
|
||||
.Callback<RouteContext>((c) => c.Handler = accept ? NullHandler : null)
|
||||
.Returns(Task.FromResult<object>(null))
|
||||
.Verifiable();
|
||||
|
||||
|
|
|
|||
|
|
@ -8,54 +8,19 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Extensions.Logging.Internal;
|
||||
using Microsoft.Extensions.Logging.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class TemplateRouteTest
|
||||
public class RouteTest
|
||||
{
|
||||
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
|
||||
private static IInlineConstraintResolver _inlineConstraintResolver = GetInlineConstraintResolver();
|
||||
|
||||
private async Task<Tuple<TestSink, RouteContext>> SetUp(
|
||||
bool loggerEnabled,
|
||||
string routeName,
|
||||
string template,
|
||||
string requestPath,
|
||||
TestSink testSink = null)
|
||||
{
|
||||
if (testSink == null)
|
||||
{
|
||||
testSink = new TestSink(
|
||||
TestSink.EnableWithTypeName<TemplateRoute>,
|
||||
TestSink.EnableWithTypeName<TemplateRoute>);
|
||||
}
|
||||
|
||||
var loggerFactory = new TestLoggerFactory(testSink, loggerEnabled);
|
||||
|
||||
TemplateRoute route;
|
||||
if (!string.IsNullOrEmpty(routeName))
|
||||
{
|
||||
route = CreateRoute(routeName, template);
|
||||
}
|
||||
else
|
||||
{
|
||||
route = CreateRoute(template);
|
||||
}
|
||||
|
||||
var context = CreateRouteContext(requestPath, loggerFactory);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
return Tuple.Create(testSink, context);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateTemplate_InlineConstraint_Regex_Malformed()
|
||||
{
|
||||
|
|
@ -67,7 +32,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
"'IInlineConstraintResolverProxy'.";
|
||||
|
||||
var exception = Assert.Throws<InvalidOperationException>(
|
||||
() => new TemplateRoute(
|
||||
() => new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -78,67 +43,6 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
Assert.Equal(expected, exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchSuccess_LogsCorrectValues()
|
||||
{
|
||||
// Arrange & Act
|
||||
var routeName = "Default";
|
||||
var template = "{controller}/{action}";
|
||||
var result = await SetUp(
|
||||
routeName: routeName,
|
||||
template: template,
|
||||
requestPath: "/Home/Index",
|
||||
loggerEnabled: true);
|
||||
var sink = result.Item1;
|
||||
var expected = "Request successfully matched the route with " +
|
||||
$"name '{routeName}' and template '{template}'.";
|
||||
|
||||
// Assert
|
||||
Assert.Empty(sink.Scopes);
|
||||
Assert.Single(sink.Writes);
|
||||
Assert.Equal(expected, sink.Writes[0].State?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchFailOnConstraints_LogsCorrectValues()
|
||||
{
|
||||
// Arrange & Act
|
||||
var sink = new TestSink((writeEnabled) => true, (scopeEnabled) => true);
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
var result = await SetUp(
|
||||
routeName: "Default",
|
||||
template: template,
|
||||
requestPath: "/Home/Index/Failure",
|
||||
loggerEnabled: true,
|
||||
testSink: sink);
|
||||
var expectedMessage = "Route value 'Failure' with key 'id' did not match the " +
|
||||
$"constraint '{typeof(IntRouteConstraint).FullName}'.";
|
||||
var expectedLogValues = new[]
|
||||
{
|
||||
new KeyValuePair<string, object>("RouteValue", "Failure"),
|
||||
new KeyValuePair<string, object>("RouteKey", "id"),
|
||||
new KeyValuePair<string, object>("RouteConstraint", typeof(IntRouteConstraint).FullName)
|
||||
};
|
||||
|
||||
// Assert
|
||||
Assert.Empty(sink.Scopes);
|
||||
Assert.Single(sink.Writes);
|
||||
var sinkWrite = sink.Writes[0];
|
||||
var formattedLogValues = Assert.IsType<FormattedLogValues>(sinkWrite.State);
|
||||
Assert.Equal(expectedMessage, formattedLogValues.ToString());
|
||||
var actualLogValues = formattedLogValues.GetValues();
|
||||
foreach(var expectedPair in expectedLogValues)
|
||||
{
|
||||
Assert.Contains(
|
||||
actualLogValues,
|
||||
(kvp) =>
|
||||
{
|
||||
return string.Equals(expectedPair.Key, kvp.Key)
|
||||
&& string.Equals(expectedPair.Value?.ToString(), kvp.Value?.ToString());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MergesExistingRouteData_IfRouteMatches()
|
||||
{
|
||||
|
|
@ -160,11 +64,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -187,12 +91,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.True(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Equal("5", context.RouteData.Values["id"]);
|
||||
Assert.NotSame(originalRouteDataValues, context.RouteData.Values);
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
|
||||
Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
|
||||
Assert.Equal("Friday", context.RouteData.DataTokens["today"]);
|
||||
Assert.NotSame(originalDataTokens, context.RouteData.DataTokens);
|
||||
Assert.NotSame(route.DataTokens, context.RouteData.DataTokens);
|
||||
Assert.Same(originalDataTokens, context.RouteData.DataTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -215,13 +118,13 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var constraint = new CapturingConstraint();
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -249,171 +152,10 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.True(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Equal("5", context.RouteData.Values["id"]);
|
||||
Assert.NotSame(originalRouteDataValues, context.RouteData.Values);
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
|
||||
Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
|
||||
Assert.Equal("Friday", context.RouteData.DataTokens["today"]);
|
||||
Assert.NotSame(originalDataTokens, context.RouteData.DataTokens);
|
||||
Assert.NotSame(route.DataTokens, context.RouteData.DataTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_CleansUpMergedRouteData_IfRouteDoesNotMatch()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = context.RouteData.Values;
|
||||
originalRouteDataValues.Add("country", "USA");
|
||||
|
||||
var originalDataTokens = context.RouteData.DataTokens;
|
||||
originalDataTokens.Add("company", "Contoso");
|
||||
|
||||
IDictionary<string, object> routeValues = null;
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = false;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
constraints: null,
|
||||
dataTokens: new RouteValueDictionary(new { today = "Friday" }),
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(routeValues);
|
||||
|
||||
Assert.True(routeValues.ContainsKey("country"));
|
||||
Assert.Equal("USA", routeValues["country"]);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.False(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
|
||||
Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
|
||||
Assert.False(context.RouteData.DataTokens.ContainsKey("today"));
|
||||
Assert.Same(originalDataTokens, context.RouteData.DataTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_CleansUpMergedRouteData_IfHandlerThrows()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = context.RouteData.Values;
|
||||
originalRouteDataValues.Add("country", "USA");
|
||||
|
||||
var originalDataTokens = context.RouteData.DataTokens;
|
||||
originalDataTokens.Add("company", "Contoso");
|
||||
|
||||
IDictionary<string, object> routeValues = null;
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = false;
|
||||
})
|
||||
.Throws(new Exception());
|
||||
|
||||
var route = new TemplateRoute(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
constraints: null,
|
||||
dataTokens: new RouteValueDictionary(new { today = "Friday" }),
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<Exception>(() => route.RouteAsync(context));
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(routeValues);
|
||||
|
||||
Assert.True(routeValues.ContainsKey("country"));
|
||||
Assert.Equal("USA", routeValues["country"]);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.False(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
|
||||
Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
|
||||
Assert.False(context.RouteData.DataTokens.ContainsKey("today"));
|
||||
Assert.Same(originalDataTokens, context.RouteData.DataTokens);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_CleansUpMergedRouteData_IfConstraintThrows()
|
||||
{
|
||||
// Arrange
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var context = CreateRouteContext("/Home/Index/5");
|
||||
var originalRouteDataValues = context.RouteData.Values;
|
||||
originalRouteDataValues.Add("country", "USA");
|
||||
|
||||
var originalDataTokens = context.RouteData.DataTokens;
|
||||
originalDataTokens.Add("company", "Contoso");
|
||||
|
||||
var mockTarget = new Mock<IRouter>(MockBehavior.Strict);
|
||||
mockTarget
|
||||
.Setup(s => s.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
ctx.IsHandled = true;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var constraint = new Mock<IRouteConstraint>(MockBehavior.Strict);
|
||||
constraint
|
||||
.Setup(c => c.Match(
|
||||
It.IsAny<HttpContext>(),
|
||||
It.IsAny<IRouter>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<IDictionary<string, object>>(),
|
||||
It.IsAny<RouteDirection>()))
|
||||
.Callback(() => { throw new Exception(); });
|
||||
|
||||
var route = new TemplateRoute(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
constraints: new RouteValueDictionary(new { action = constraint.Object }),
|
||||
dataTokens: new RouteValueDictionary(new { today = "Friday" }),
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
|
||||
// Act
|
||||
var ex = await Assert.ThrowsAsync<Exception>(() => route.RouteAsync(context));
|
||||
|
||||
// Assert
|
||||
Assert.True(context.RouteData.Values.ContainsKey("country"));
|
||||
Assert.Equal("USA", context.RouteData.Values["country"]);
|
||||
Assert.False(context.RouteData.Values.ContainsKey("id"));
|
||||
Assert.Same(originalRouteDataValues, context.RouteData.Values);
|
||||
|
||||
Assert.Equal("Contoso", context.RouteData.DataTokens["company"]);
|
||||
Assert.False(context.RouteData.DataTokens.ContainsKey("today"));
|
||||
Assert.Same(originalDataTokens, context.RouteData.DataTokens);
|
||||
}
|
||||
|
||||
|
|
@ -432,11 +174,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -451,7 +193,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
|
|
@ -474,11 +216,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -493,7 +235,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.True(routeValues.ContainsKey("ssn"));
|
||||
Assert.Equal("123-456-7890", routeValues["ssn"]);
|
||||
|
||||
|
|
@ -516,11 +258,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -535,7 +277,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.NotNull(routeValues);
|
||||
Assert.False(routeValues.ContainsKey("id"));
|
||||
Assert.False(context.RouteData.Values.ContainsKey("id"));
|
||||
|
|
@ -556,14 +298,14 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var constraints = new Dictionary<string, object>();
|
||||
constraints.Add("id", new RangeRouteConstraint(1, 20));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -585,7 +327,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.True(routeValues.ContainsKey("id"));
|
||||
Assert.Equal("5", routeValues["id"]);
|
||||
|
||||
|
|
@ -608,11 +350,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Callback<RouteContext>(ctx =>
|
||||
{
|
||||
routeValues = ctx.RouteData.Values;
|
||||
ctx.IsHandled = true;
|
||||
ctx.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
|
||||
var route = new TemplateRoute(
|
||||
var route = new Route(
|
||||
mockTarget.Object,
|
||||
template,
|
||||
defaults: null,
|
||||
|
|
@ -627,7 +369,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
}
|
||||
|
||||
#region Route Matching
|
||||
|
|
@ -644,7 +386,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(2, context.RouteData.Values.Count);
|
||||
Assert.Equal("Home", context.RouteData.Values["controller"]);
|
||||
Assert.Equal("Index", context.RouteData.Values["action"]);
|
||||
|
|
@ -661,7 +403,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(0, context.RouteData.Values.Count);
|
||||
}
|
||||
|
||||
|
|
@ -676,7 +418,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(2, context.RouteData.Values.Count);
|
||||
Assert.Equal("Home", context.RouteData.Values["controller"]);
|
||||
Assert.Equal("Index", context.RouteData.Values["action"]);
|
||||
|
|
@ -695,7 +437,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
|
||||
// This should not affect the route - RouteData.DataTokens is a copy
|
||||
context.RouteData.DataTokens.Add("company", "contoso");
|
||||
|
|
@ -716,7 +458,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -730,25 +472,11 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
|
||||
// Issue #16 tracks this.
|
||||
Assert.Empty(context.RouteData.Values);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Match_RejectedByHandler_ClearsRouters()
|
||||
{
|
||||
// Arrange
|
||||
var route = CreateRoute("{controller}", handleRequest: false);
|
||||
var context = CreateRouteContext("/Home");
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Empty(context.RouteData.Routers);
|
||||
var value = Assert.Single(context.RouteData.Values);
|
||||
Assert.Equal("controller", value.Key);
|
||||
Assert.Equal("Home", Assert.IsType<string>(value.Value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -763,7 +491,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(1, context.RouteData.Routers.Count);
|
||||
Assert.Same(target, context.RouteData.Routers[0]);
|
||||
}
|
||||
|
|
@ -793,7 +521,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(3, context.RouteData.Values.Count);
|
||||
Assert.Equal("Home", context.RouteData.Values["controller"]);
|
||||
Assert.Equal("Create", context.RouteData.Values["action"]);
|
||||
|
|
@ -811,7 +539,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(2, context.RouteData.Values.Count);
|
||||
Assert.Equal("Home", context.RouteData.Values["controller"]);
|
||||
Assert.Equal("Create", context.RouteData.Values["action"]);
|
||||
|
|
@ -828,7 +556,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(3, context.RouteData.Values.Count);
|
||||
Assert.Equal("Home", context.RouteData.Values["controller"]);
|
||||
Assert.Equal("Create", context.RouteData.Values["action"]);
|
||||
|
|
@ -846,7 +574,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
}
|
||||
|
||||
private static RouteContext CreateRouteContext(string requestPath, ILoggerFactory factory = null)
|
||||
|
|
@ -1021,14 +749,14 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
// Arrange
|
||||
var context = CreateVirtualPathContext(new { p1 = "abcd" });
|
||||
|
||||
TemplateRoute r = CreateRoute(
|
||||
var route = CreateRoute(
|
||||
"{p1}/{p2}",
|
||||
new { p2 = "catchall" },
|
||||
true,
|
||||
new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// Act
|
||||
var virtualPath = r.GetVirtualPath(context);
|
||||
var virtualPath = route.GetVirtualPath(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(virtualPath);
|
||||
|
|
@ -1062,14 +790,14 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
// Arrange
|
||||
var context = CreateVirtualPathContext(new { p1 = "abcd" });
|
||||
|
||||
TemplateRoute r = CreateRoute(
|
||||
var route = CreateRoute(
|
||||
"{p1}/{*p2}",
|
||||
new { p2 = "catchall" },
|
||||
true,
|
||||
new RouteValueDictionary(new { p2 = "\\d{4}" }));
|
||||
|
||||
// Act
|
||||
var virtualPath = r.GetVirtualPath(context);
|
||||
var virtualPath = route.GetVirtualPath(context);
|
||||
|
||||
// Assert
|
||||
Assert.Null(virtualPath);
|
||||
|
|
@ -1081,7 +809,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
// Arrange
|
||||
var context = CreateVirtualPathContext(new { p1 = "hello", p2 = "1234" });
|
||||
|
||||
TemplateRoute route = CreateRoute(
|
||||
var route = CreateRoute(
|
||||
"{p1}/{*p2}",
|
||||
new { p2 = "catchall" },
|
||||
true,
|
||||
|
|
@ -1112,7 +840,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
.Returns(true)
|
||||
.Verifiable();
|
||||
|
||||
TemplateRoute route = CreateRoute(
|
||||
var route = CreateRoute(
|
||||
"{p1}/{p2}",
|
||||
new { p2 = "catchall" },
|
||||
true,
|
||||
|
|
@ -1623,7 +1351,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
dataTokens: dataToken);
|
||||
|
||||
// Assert
|
||||
var templateRoute = (TemplateRoute)routeBuilder.Routes[0];
|
||||
var templateRoute = (Route)routeBuilder.Routes[0];
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDictionary.Count, templateRoute.DataTokens.Count);
|
||||
|
|
@ -1664,7 +1392,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
defaults: null,
|
||||
constraints: new { controller = "a.*", action = mockConstraint });
|
||||
|
||||
var constraints = ((TemplateRoute)routeBuilder.Routes[0]).Constraints;
|
||||
var constraints = ((Route)routeBuilder.Routes[0]).Constraints;
|
||||
|
||||
// Assert
|
||||
Assert.Equal(2, constraints.Count);
|
||||
|
|
@ -1685,7 +1413,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
constraints: new { id = "1*" });
|
||||
|
||||
// Assert
|
||||
var constraints = ((TemplateRoute)routeBuilder.Routes[0]).Constraints;
|
||||
var constraints = ((Route)routeBuilder.Routes[0]).Constraints;
|
||||
Assert.Equal(1, constraints.Count);
|
||||
var constraint = (CompositeRouteConstraint)constraints["id"];
|
||||
Assert.IsType<CompositeRouteConstraint>(constraint);
|
||||
|
|
@ -1706,7 +1434,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
constraints: null);
|
||||
|
||||
// Assert
|
||||
var constraints = ((TemplateRoute)routeBuilder.Routes[0]).Constraints;
|
||||
var constraints = ((Route)routeBuilder.Routes[0]).Constraints;
|
||||
Assert.Equal(1, constraints.Count);
|
||||
Assert.IsType<IntRouteConstraint>(constraints["id"]);
|
||||
}
|
||||
|
|
@ -1720,7 +1448,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
routeBuilder.MapRoute(name: "RouteName", template: "{controller}/{action}", defaults: null);
|
||||
|
||||
// Act
|
||||
var name = ((TemplateRoute)routeBuilder.Routes[0]).Name;
|
||||
var name = ((Route)routeBuilder.Routes[0]).Name;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("RouteName", name);
|
||||
|
|
@ -1738,7 +1466,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
constraints: null);
|
||||
|
||||
// Act
|
||||
var name = ((TemplateRoute)routeBuilder.Routes[0]).Name;
|
||||
var name = ((Route)routeBuilder.Routes[0]).Name;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("RouteName", name);
|
||||
|
|
@ -1761,7 +1489,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
{
|
||||
var routeBuilder = new RouteBuilder();
|
||||
|
||||
routeBuilder.DefaultHandler = new Mock<IRouter>().Object;
|
||||
routeBuilder.DefaultHandler = new RouteHandler(NullHandler);
|
||||
|
||||
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||
serviceProviderMock.Setup(o => o.GetService(typeof(IInlineConstraintResolver)))
|
||||
|
|
@ -1771,9 +1499,9 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return routeBuilder;
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateRoute(string routeName, string template, bool handleRequest = true)
|
||||
private static Route CreateRoute(string routeName, string template, bool handleRequest = true)
|
||||
{
|
||||
return new TemplateRoute(
|
||||
return new Route(
|
||||
CreateTarget(handleRequest),
|
||||
routeName,
|
||||
template,
|
||||
|
|
@ -1783,49 +1511,51 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateRoute(string template, bool handleRequest = true)
|
||||
private static Route CreateRoute(string template, bool handleRequest = true)
|
||||
{
|
||||
return new TemplateRoute(CreateTarget(handleRequest), template, _inlineConstraintResolver);
|
||||
return new Route(CreateTarget(handleRequest), template, _inlineConstraintResolver);
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateRoute(string template,
|
||||
object defaults,
|
||||
bool handleRequest = true,
|
||||
object constraints = null,
|
||||
object dataTokens = null)
|
||||
private static Route CreateRoute(
|
||||
string template,
|
||||
object defaults,
|
||||
bool handleRequest = true,
|
||||
object constraints = null,
|
||||
object dataTokens = null)
|
||||
{
|
||||
return new TemplateRoute(CreateTarget(handleRequest),
|
||||
template,
|
||||
new RouteValueDictionary(defaults),
|
||||
(constraints as IDictionary<string, object>) ??
|
||||
new RouteValueDictionary(constraints),
|
||||
(dataTokens as IDictionary<string, object>) ??
|
||||
new RouteValueDictionary(dataTokens),
|
||||
_inlineConstraintResolver);
|
||||
return new Route(
|
||||
CreateTarget(handleRequest),
|
||||
template,
|
||||
new RouteValueDictionary(defaults),
|
||||
new RouteValueDictionary(constraints),
|
||||
new RouteValueDictionary(dataTokens),
|
||||
_inlineConstraintResolver);
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateRoute(IRouter target, string template)
|
||||
private static Route CreateRoute(IRouter target, string template)
|
||||
{
|
||||
return new TemplateRoute(target,
|
||||
template,
|
||||
new RouteValueDictionary(),
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
return new Route(
|
||||
target,
|
||||
template,
|
||||
new RouteValueDictionary(),
|
||||
constraints: null,
|
||||
dataTokens: null,
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
}
|
||||
|
||||
private static TemplateRoute CreateRoute(
|
||||
private static Route CreateRoute(
|
||||
IRouter target,
|
||||
string template,
|
||||
object defaults,
|
||||
RouteValueDictionary dataTokens = null)
|
||||
{
|
||||
return new TemplateRoute(target,
|
||||
template,
|
||||
new RouteValueDictionary(defaults),
|
||||
constraints: null,
|
||||
dataTokens: dataTokens,
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
return new Route(
|
||||
target,
|
||||
template,
|
||||
new RouteValueDictionary(defaults),
|
||||
constraints: null,
|
||||
dataTokens: dataTokens,
|
||||
inlineConstraintResolver: _inlineConstraintResolver);
|
||||
}
|
||||
|
||||
private static IRouter CreateTarget(bool handleRequest = true)
|
||||
|
|
@ -1837,7 +1567,7 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
|
||||
target
|
||||
.Setup(e => e.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) => c.IsHandled = handleRequest)
|
||||
.Callback<RouteContext>((c) => c.Handler = handleRequest ? NullHandler : null)
|
||||
.Returns(Task.FromResult<object>(null));
|
||||
|
||||
return target.Object;
|
||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
public Task RouteAsync(RouteContext context)
|
||||
{
|
||||
context.IsHandled = _isHandled;
|
||||
context.Handler = _isHandled ? (RequestDelegate)((c) => Task.FromResult(0)) : null;
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Microsoft.AspNet.Routing.Tests
|
|||
constraints: null);
|
||||
|
||||
// Assert
|
||||
var defaults = ((Template.TemplateRoute)routeBuilder.Routes[0]).Defaults;
|
||||
var defaults = ((Route)routeBuilder.Routes[0]).Defaults;
|
||||
Assert.Equal("12", defaults["id"]);
|
||||
}
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNet.Routing.Tests
|
|||
constraints: null);
|
||||
|
||||
// Assert
|
||||
var defaults = ((Template.TemplateRoute)routeBuilder.Routes[0]).Defaults;
|
||||
var defaults = ((Route)routeBuilder.Routes[0]).Defaults;
|
||||
Assert.Equal(value, defaults["p1"]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
{
|
||||
public class TreeRouterTest
|
||||
{
|
||||
private static readonly RequestDelegate NullHandler = (c) => Task.FromResult(0);
|
||||
|
||||
[Theory]
|
||||
[InlineData("template/5", "template/{parameter:int}")]
|
||||
[InlineData("template/5", "template/{parameter}")]
|
||||
|
|
@ -36,15 +38,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, firstTemplate);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, firstTemplate, order: 0);
|
||||
var secondRoute = CreateMatchingEntry(next.Object, secondTemplate, order: 0);
|
||||
|
|
@ -84,15 +83,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, secondTemplate);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, firstTemplate, order: 1);
|
||||
var secondRoute = CreateMatchingEntry(next.Object, secondTemplate, order: 0);
|
||||
|
|
@ -126,15 +122,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, template, order: 1);
|
||||
var secondRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||
|
|
@ -166,15 +159,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, first);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var secondRouter = new Mock<IRouter>(MockBehavior.Strict);
|
||||
|
||||
|
|
@ -211,15 +201,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||
|
||||
|
|
@ -239,12 +226,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Assert
|
||||
if (expectedResult)
|
||||
{
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
Assert.Equal(expectedRouteGroup, context.RouteData.Values["test_route_group"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,15 +264,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||
|
||||
|
|
@ -300,7 +284,7 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.True(context.IsHandled);
|
||||
Assert.NotNull(context.Handler);
|
||||
if (p1 != null)
|
||||
{
|
||||
Assert.Equal(p1, context.RouteData.Values["p1"]);
|
||||
|
|
@ -335,15 +319,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
// Arrange
|
||||
var expectedRouteGroup = string.Format("{0}&&{1}", 0, template);
|
||||
|
||||
// We need to force the creation of a closure in order to avoid an issue with Moq and Roslyn.
|
||||
var numberOfCalls = 0;
|
||||
Action<RouteContext> callBack = ctx => { ctx.IsHandled = true; numberOfCalls++; };
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback(callBack)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var firstRoute = CreateMatchingEntry(next.Object, template, order: 0);
|
||||
|
||||
|
|
@ -359,7 +340,7 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(context.IsHandled);
|
||||
Assert.Null(context.Handler);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -1358,15 +1339,17 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
{
|
||||
// Arrange
|
||||
RouteData nestedRouteData = null;
|
||||
|
||||
var next = new Mock<IRouter>();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) =>
|
||||
.Callback<RouteContext>(c =>
|
||||
{
|
||||
nestedRouteData = c.RouteData;
|
||||
c.IsHandled = true;
|
||||
c.Handler = NullHandler;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var entry = CreateMatchingEntry(next.Object, "api/Store", order: 0);
|
||||
var route = CreateAttributeRoute(next.Object, entry);
|
||||
|
|
@ -1399,11 +1382,9 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
var router = new Mock<IRouter>();
|
||||
router
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) =>
|
||||
{
|
||||
c.IsHandled = true;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var entry = CreateMatchingEntry(router.Object, "Foo/{*path}", order: 0);
|
||||
var route = CreateAttributeRoute(router.Object, entry);
|
||||
|
|
@ -1427,11 +1408,9 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
var router = new Mock<IRouter>();
|
||||
router
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) =>
|
||||
{
|
||||
c.IsHandled = true;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
.Callback<RouteContext>(c => c.Handler = NullHandler)
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var entry = CreateMatchingEntry(router.Object, "Foo/{*path}", order: 0);
|
||||
var route = CreateAttributeRoute(router.Object, entry);
|
||||
|
|
@ -1456,12 +1435,12 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
var next = new Mock<IRouter>();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) =>
|
||||
.Callback<RouteContext>(c =>
|
||||
{
|
||||
nestedRouteData = c.RouteData;
|
||||
c.IsHandled = false;
|
||||
})
|
||||
.Returns(Task.FromResult(true));
|
||||
.Returns(Task.FromResult(true))
|
||||
.Verifiable();
|
||||
|
||||
var entry = CreateMatchingEntry(next.Object, "api/Store", order: 0);
|
||||
var route = CreateAttributeRoute(next.Object, entry);
|
||||
|
|
@ -1499,10 +1478,9 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
var next = new Mock<IRouter>();
|
||||
next
|
||||
.Setup(r => r.RouteAsync(It.IsAny<RouteContext>()))
|
||||
.Callback<RouteContext>((c) =>
|
||||
.Callback<RouteContext>(c =>
|
||||
{
|
||||
nestedRouteData = c.RouteData;
|
||||
c.IsHandled = false;
|
||||
})
|
||||
.Throws(new Exception());
|
||||
|
||||
|
|
@ -1755,7 +1733,7 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
return builder.Build(version: 1);
|
||||
}
|
||||
|
||||
private static IReadOnlyDictionary<string, IRouteConstraint> GetRouteConstriants(
|
||||
private static IDictionary<string, IRouteConstraint> GetRouteConstriants(
|
||||
IInlineConstraintResolver inlineConstraintResolver,
|
||||
string template,
|
||||
RouteTemplate parsedRouteTemplate)
|
||||
|
|
@ -1797,11 +1775,11 @@ namespace Microsoft.AspNet.Routing.Tree
|
|||
{
|
||||
if (MatchingDelegate == null)
|
||||
{
|
||||
context.IsHandled = true;
|
||||
context.Handler = NullHandler;
|
||||
}
|
||||
else
|
||||
{
|
||||
context.IsHandled = MatchingDelegate(context);
|
||||
context.Handler = MatchingDelegate(context) ? NullHandler : null;
|
||||
}
|
||||
|
||||
return Task.FromResult(true);
|
||||
|
|
|
|||
Loading…
Reference in New Issue