Routing Logging
Added scoped logging to RouterMiddleware, RouteCollection, and TemplateRoute.
This commit is contained in:
parent
61436fb7d1
commit
fca9831115
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Routing.Logging
|
||||
{
|
||||
public static class LogFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// A formatter for use with <see cref="Microsoft.Framework.Logging.ILogger.WriteCore(
|
||||
/// Framework.Logging.TraceType,
|
||||
/// int,
|
||||
/// object,
|
||||
/// Exception, Func{object, Exception, string})"/>.
|
||||
/// </summary>
|
||||
public static string Formatter(object o, Exception e)
|
||||
{
|
||||
if (o != null && e != null)
|
||||
{
|
||||
return o + Environment.NewLine + e;
|
||||
}
|
||||
|
||||
if (o != null)
|
||||
{
|
||||
return o.ToString();
|
||||
}
|
||||
|
||||
if (e != null)
|
||||
{
|
||||
return e.ToString();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
internal static class LoggerExtensions
|
||||
{
|
||||
public static bool WriteValues([NotNull] this ILogger logger, object values)
|
||||
{
|
||||
return logger.WriteCore(
|
||||
eventType: TraceType.Information,
|
||||
eventId: 0,
|
||||
state: values,
|
||||
exception: null,
|
||||
formatter: LogFormatter.Formatter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the state of
|
||||
/// <see cref="Microsoft.AspNet.Routing.RouteCollection.RouteAsync(RouteContext)"/>.
|
||||
/// </summary>
|
||||
public class RouteCollectionRouteAsyncValues
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the state.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "RouteCollection.RouteAsync";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The available routes.
|
||||
/// </summary>
|
||||
public IList<IRouter> Routes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the request is handled.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A summary of the data for display.
|
||||
/// </summary>
|
||||
public string Summary
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(Name);
|
||||
builder.Append("\tRoutes: ");
|
||||
StringBuilderHelpers.Append(builder, Routes);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tHandled? ");
|
||||
builder.Append(Handled);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the state of <see cref="RouteConstraintMatcher.Match(
|
||||
/// System.Collections.Generic.IDictionary{string, IRouteConstraint},
|
||||
/// System.Collections.Generic.IDictionary{string, object},
|
||||
/// Http.HttpContext,
|
||||
/// IRouter,
|
||||
/// RouteDirection,
|
||||
/// Framework.Logging.ILogger)"/>.
|
||||
/// </summary>
|
||||
public class RouteConstraintMatcherMatchValues
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the state.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "RouteConstraintMatcher.Match";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key of the constraint.
|
||||
/// </summary>
|
||||
public string ConstraintKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The constraint.
|
||||
/// </summary>
|
||||
public IRouteConstraint Constraint { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the <see cref="Constraint"/> matched.
|
||||
/// </summary>
|
||||
public bool Matched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A summary of the data for display.
|
||||
/// </summary>
|
||||
public string Summary
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(Name);
|
||||
builder.Append("\tConstraint key: ");
|
||||
builder.AppendLine(ConstraintKey);
|
||||
builder.Append("\tConstraint: ");
|
||||
builder.Append(Constraint);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tMatched? ");
|
||||
builder.Append(Matched);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the state of
|
||||
/// <see cref="Microsoft.AspNet.Builder.RouterMiddleware.Invoke(Http.HttpContext)"/>.
|
||||
/// </summary>
|
||||
public class RouterMiddlewareInvokeValues
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the state.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "RouterMiddleware.Invoke";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if the request is handled.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A summary of the data for display.
|
||||
/// </summary>
|
||||
public string Summary
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(Name);
|
||||
builder.Append("\tHandled? ");
|
||||
builder.Append(Handled);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
internal static class StringBuilderHelpers
|
||||
{
|
||||
public static void Append<T>(StringBuilder builder, IEnumerable<T> items)
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append("\t\t");
|
||||
builder.Append(item != null ? item.ToString() : "null");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Append<K, V>(StringBuilder builder, IDictionary<K, V> dict)
|
||||
{
|
||||
if (dict == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var kvp in dict)
|
||||
{
|
||||
builder.Append(Environment.NewLine);
|
||||
builder.Append("\t\t");
|
||||
builder.Append(kvp.Key != null ? kvp.Key.ToString() : "null");
|
||||
builder.Append(" : ");
|
||||
builder.Append(kvp.Value != null ? kvp.Value.ToString() : "null");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Text;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Logging
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the state of
|
||||
/// <see cref="Microsoft.AspNet.Routing.Template.TemplateRoute.RouteAsync(RouteContext)"/>.
|
||||
/// </summary>
|
||||
public class TemplateRouteRouteAsyncValues
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the state.
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "TemplateRoute.RouteAsync";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The target.
|
||||
/// </summary>
|
||||
public IRouter Target { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The template.
|
||||
/// </summary>
|
||||
public string Template { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The request path.
|
||||
/// </summary>
|
||||
public string RequestPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The values produced by default.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> DefaultValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The values produced from the request.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> ProducedValues { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The constraints matched on the produced values.
|
||||
/// </summary>
|
||||
public IDictionary<string, IRouteConstraint> Constraints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the <see cref="ProducedValues"/> matched.
|
||||
/// </summary>
|
||||
public bool MatchedTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the <see cref="Constraints"/> matched.
|
||||
/// </summary>
|
||||
public bool MatchedConstraints { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if this route matched.
|
||||
/// </summary>
|
||||
public bool Matched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the request is handled.
|
||||
/// </summary>
|
||||
public bool Handled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A summary of the data for display.
|
||||
/// </summary>
|
||||
public string Summary
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.AppendLine(Name);
|
||||
builder.Append("\tTarget: ");
|
||||
builder.Append(Target);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tTemplate: ");
|
||||
builder.AppendLine(Template);
|
||||
builder.Append("\tRequest path: ");
|
||||
builder.AppendLine(RequestPath);
|
||||
builder.Append("\tDefault values: ");
|
||||
StringBuilderHelpers.Append(builder, DefaultValues);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tProduced values: ");
|
||||
StringBuilderHelpers.Append(builder, ProducedValues);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tConstraints: ");
|
||||
StringBuilderHelpers.Append(builder, Constraints);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tMatched template? ");
|
||||
builder.Append(MatchedTemplate);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tMatched constraints? ");
|
||||
builder.Append(MatchedConstraints);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tMatched? ");
|
||||
builder.Append(Matched);
|
||||
builder.AppendLine();
|
||||
builder.Append("\tHandled? ");
|
||||
builder.Append(Handled);
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return Summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,10 +47,17 @@
|
|||
<Compile Include="INamedRouter.cs" />
|
||||
<Compile Include="InlineRouteParameterParser.cs" />
|
||||
<Compile Include="IRouteBuilder.cs" />
|
||||
<Compile Include="Logging\LoggerExtensions.cs" />
|
||||
<Compile Include="Logging\RouteConstraintMatcherMatchValues.cs" />
|
||||
<Compile Include="Logging\StringBuilderHelpers.cs" />
|
||||
<Compile Include="RouteOptions.cs" />
|
||||
<Compile Include="IRouteCollection.cs" />
|
||||
<Compile Include="IRouteConstraint.cs" />
|
||||
<Compile Include="IRouter.cs" />
|
||||
<Compile Include="Logging\RouteCollectionRouteAsyncValues.cs" />
|
||||
<Compile Include="Logging\RouterMiddlewareInvokeValues.cs" />
|
||||
<Compile Include="Logging\TemplateRouteRouteAsyncValues.cs" />
|
||||
<Compile Include="Logging\LogFormatter.cs" />
|
||||
<Compile Include="NotNullAttribute.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs" />
|
||||
<Compile Include="RouteBuilder.cs" />
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
|
|
@ -11,8 +15,9 @@ namespace Microsoft.AspNet.Routing
|
|||
{
|
||||
private readonly List<IRouter> _routes = new List<IRouter>();
|
||||
private readonly List<IRouter> _unnamedRoutes = new List<IRouter>();
|
||||
private readonly Dictionary<string, INamedRouter> _namedRoutes =
|
||||
private readonly Dictionary<string, INamedRouter> _namedRoutes =
|
||||
new Dictionary<string, INamedRouter>(StringComparer.OrdinalIgnoreCase);
|
||||
private ILogger _logger;
|
||||
|
||||
public IRouter this[int index]
|
||||
{
|
||||
|
|
@ -44,14 +49,27 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
public async virtual Task RouteAsync(RouteContext context)
|
||||
{
|
||||
for (var i = 0; i < Count; i++)
|
||||
EnsureLogger(context.HttpContext);
|
||||
using (_logger.BeginScope("RouteCollection.RouteAsync"))
|
||||
{
|
||||
var route = this[i];
|
||||
|
||||
await route.RouteAsync(context);
|
||||
if (context.IsHandled)
|
||||
for (var i = 0; i < Count; i++)
|
||||
{
|
||||
return;
|
||||
var route = this[i];
|
||||
|
||||
await route.RouteAsync(context);
|
||||
if (context.IsHandled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(new RouteCollectionRouteAsyncValues()
|
||||
{
|
||||
Handled = context.IsHandled,
|
||||
Routes = _routes
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,5 +116,14 @@ namespace Microsoft.AspNet.Routing
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void EnsureLogger(HttpContext context)
|
||||
{
|
||||
if (_logger == null)
|
||||
{
|
||||
var factory = context.RequestServices.GetService<ILoggerFactory>();
|
||||
_logger = factory.Create<RouteCollection>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
|
|
@ -12,7 +14,8 @@ namespace Microsoft.AspNet.Routing
|
|||
[NotNull] IDictionary<string, object> routeValues,
|
||||
[NotNull] HttpContext httpContext,
|
||||
[NotNull] IRouter route,
|
||||
[NotNull] RouteDirection routeDirection)
|
||||
[NotNull] RouteDirection routeDirection,
|
||||
[NotNull] ILogger logger)
|
||||
{
|
||||
if (constraints == null)
|
||||
{
|
||||
|
|
@ -24,8 +27,30 @@ namespace Microsoft.AspNet.Routing
|
|||
var constraint = kvp.Value;
|
||||
if (!constraint.Match(httpContext, route, kvp.Key, routeValues, routeDirection))
|
||||
{
|
||||
if (routeDirection.Equals(RouteDirection.IncomingRequest)
|
||||
&& logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
logger.WriteValues(new RouteConstraintMatcherMatchValues()
|
||||
{
|
||||
ConstraintKey = kvp.Key,
|
||||
Constraint = kvp.Value,
|
||||
Matched = false
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (routeDirection.Equals(RouteDirection.IncomingRequest)
|
||||
&& logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
logger.WriteValues(new RouteConstraintMatcherMatchValues()
|
||||
{
|
||||
ConstraintKey = kvp.Key,
|
||||
Constraint = kvp.Value,
|
||||
Matched = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Builder
|
||||
{
|
||||
public class RouterMiddleware
|
||||
{
|
||||
private ILogger _logger;
|
||||
|
||||
public RouterMiddleware(RequestDelegate next, IRouter router)
|
||||
{
|
||||
Next = next;
|
||||
|
|
@ -29,13 +34,32 @@ namespace Microsoft.AspNet.Builder
|
|||
|
||||
public async Task Invoke(HttpContext httpContext)
|
||||
{
|
||||
var context = new RouteContext(httpContext);
|
||||
context.RouteData.Routers.Add(Router);
|
||||
|
||||
await Router.RouteAsync(context);
|
||||
if (!context.IsHandled)
|
||||
EnsureLogger(httpContext);
|
||||
using (_logger.BeginScope("RouterMiddleware.Invoke"))
|
||||
{
|
||||
await Next.Invoke(httpContext);
|
||||
var context = new RouteContext(httpContext);
|
||||
context.RouteData.Routers.Add(Router);
|
||||
|
||||
await Router.RouteAsync(context);
|
||||
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(new RouterMiddlewareInvokeValues() { Handled = context.IsHandled });
|
||||
}
|
||||
|
||||
if (!context.IsHandled)
|
||||
{
|
||||
await Next.Invoke(httpContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureLogger(HttpContext context)
|
||||
{
|
||||
if (_logger == null)
|
||||
{
|
||||
var factory = context.RequestServices.GetService<ILoggerFactory>();
|
||||
_logger = factory.Create<RouterMiddleware>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
|
|
@ -17,6 +20,8 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
private readonly string _routeTemplate;
|
||||
private readonly TemplateMatcher _matcher;
|
||||
private readonly TemplateBinder _binder;
|
||||
private ILogger _logger;
|
||||
private ILogger _constraintLogger;
|
||||
|
||||
public TemplateRoute(IRouter target, string routeTemplate, IInlineConstraintResolver inlineConstraintResolver)
|
||||
: this(target, routeTemplate, null, null, inlineConstraintResolver)
|
||||
|
|
@ -73,30 +78,69 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
|
||||
public async virtual Task RouteAsync([NotNull] RouteContext context)
|
||||
{
|
||||
var requestPath = context.HttpContext.Request.Path.Value;
|
||||
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
|
||||
EnsureLoggers(context.HttpContext);
|
||||
using (_logger.BeginScope("TemplateRoute.RouteAsync"))
|
||||
{
|
||||
requestPath = requestPath.Substring(1);
|
||||
}
|
||||
var requestPath = context.HttpContext.Request.Path.Value;
|
||||
|
||||
var values = _matcher.Match(requestPath, Defaults);
|
||||
if (values == null)
|
||||
{
|
||||
// If we got back a null value set, that means the URI did not match
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
||||
context.RouteData.Values = values;
|
||||
|
||||
if (RouteConstraintMatcher.Match(Constraints,
|
||||
values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest))
|
||||
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
|
||||
{
|
||||
await _target.RouteAsync(context);
|
||||
requestPath = requestPath.Substring(1);
|
||||
}
|
||||
|
||||
var values = _matcher.Match(requestPath, Defaults);
|
||||
|
||||
if (values == null)
|
||||
{
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: false,
|
||||
matchedConstraints: false,
|
||||
handled: context.IsHandled));
|
||||
}
|
||||
|
||||
// If we got back a null value set, that means the URI did not match
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
|
||||
context.RouteData.Values = values;
|
||||
|
||||
if (RouteConstraintMatcher.Match(Constraints,
|
||||
values,
|
||||
context.HttpContext,
|
||||
this,
|
||||
RouteDirection.IncomingRequest,
|
||||
_constraintLogger))
|
||||
{
|
||||
await _target.RouteAsync(context);
|
||||
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: true,
|
||||
handled: context.IsHandled));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_logger.IsEnabled(TraceType.Information))
|
||||
{
|
||||
_logger.WriteValues(CreateRouteAsyncValues(
|
||||
requestPath,
|
||||
values,
|
||||
matchedValues: true,
|
||||
matchedConstraints: false,
|
||||
handled: context.IsHandled));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,11 +154,13 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
return null;
|
||||
}
|
||||
|
||||
EnsureLoggers(context.Context);
|
||||
if (!RouteConstraintMatcher.Match(Constraints,
|
||||
values.CombinedValues,
|
||||
context.Context,
|
||||
this,
|
||||
RouteDirection.UrlGeneration))
|
||||
RouteDirection.UrlGeneration,
|
||||
_constraintLogger))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
@ -208,5 +254,41 @@ namespace Microsoft.AspNet.Routing.Template
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TemplateRouteRouteAsyncValues CreateRouteAsyncValues(
|
||||
string requestPath,
|
||||
IDictionary<string, object> producedValues,
|
||||
bool matchedValues,
|
||||
bool matchedConstraints,
|
||||
bool handled)
|
||||
{
|
||||
var values = new TemplateRouteRouteAsyncValues();
|
||||
values.Template = _routeTemplate;
|
||||
values.RequestPath = requestPath;
|
||||
values.DefaultValues = Defaults;
|
||||
values.ProducedValues = producedValues;
|
||||
values.Constraints = _constraints;
|
||||
values.Target = _target;
|
||||
values.MatchedTemplate = matchedValues;
|
||||
values.MatchedConstraints = matchedConstraints;
|
||||
values.Matched = matchedValues && matchedConstraints;
|
||||
values.Handled = handled;
|
||||
return values;
|
||||
}
|
||||
|
||||
private void EnsureLoggers(HttpContext context)
|
||||
{
|
||||
if (_logger == null)
|
||||
{
|
||||
var factory = context.RequestServices.GetService<ILoggerFactory>();
|
||||
_logger = factory.Create<TemplateRoute>();
|
||||
_constraintLogger = factory.Create(typeof(RouteConstraintMatcher).FullName);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _routeTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"Microsoft.AspNet.Http": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
||||
"Microsoft.AspNet.RequestContainer": "1.0.0-*",
|
||||
"Microsoft.Framework.DependencyInjection" : "1.0.0-*",
|
||||
"Microsoft.Framework.Logging": "1.0.0-*",
|
||||
"Microsoft.Framework.OptionsModel": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,313 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
#if NET45
|
||||
using Moq;
|
||||
#endif
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class ConstraintMatcherTest
|
||||
{
|
||||
#if NET45
|
||||
[Fact]
|
||||
public void MatchUrlGeneration_DoesNotLogData()
|
||||
{
|
||||
// Arrange
|
||||
var name = "name";
|
||||
|
||||
var sink = new TestSink();
|
||||
var logger = new TestLogger(name, sink);
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
// Act
|
||||
RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.UrlGeneration,
|
||||
logger: logger);
|
||||
|
||||
// Assert
|
||||
// There are no BeginScopes called.
|
||||
Assert.Equal(0, sink.Scopes.Count);
|
||||
|
||||
// There are no WriteCores called.
|
||||
Assert.Equal(0, sink.Writes.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchFail_LogsCorrectData()
|
||||
{
|
||||
// Arrange
|
||||
var name = "name";
|
||||
|
||||
var sink = new TestSink();
|
||||
var logger = new TestLogger(name, sink);
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
// Act
|
||||
RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: logger);
|
||||
|
||||
// Assert
|
||||
// There are no begin scopes called.
|
||||
Assert.Equal(0, sink.Scopes.Count);
|
||||
|
||||
// There are two records for IsEnabled and two for WriteCore.
|
||||
Assert.Equal(4, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(name, enabled.LoggerName);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(name, write.LoggerName);
|
||||
var values = Assert.IsType<RouteConstraintMatcherMatchValues>(write.State);
|
||||
Assert.Equal("RouteConstraintMatcher.Match", values.Name);
|
||||
Assert.Equal("a", values.ConstraintKey);
|
||||
Assert.Equal(constraints["a"], values.Constraint);
|
||||
Assert.Equal(true, values.Matched);
|
||||
|
||||
enabled = sink.Writes[2];
|
||||
Assert.Equal(name, enabled.LoggerName);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
write = sink.Writes[3];
|
||||
Assert.Equal(name, write.LoggerName);
|
||||
values = Assert.IsType<RouteConstraintMatcherMatchValues>(write.State);
|
||||
Assert.Equal("RouteConstraintMatcher.Match", values.Name);
|
||||
Assert.Equal("b", values.ConstraintKey);
|
||||
Assert.Equal(constraints["b"], values.Constraint);
|
||||
Assert.Equal(false, values.Matched);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MatchSuccess_LogsCorrectData()
|
||||
{
|
||||
// Arrange
|
||||
var name = "name";
|
||||
|
||||
var sink = new TestSink();
|
||||
var logger = new TestLogger(name, sink);
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new PassConstraint()}
|
||||
};
|
||||
|
||||
// Act
|
||||
RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: logger);
|
||||
|
||||
// Assert
|
||||
// There are no begin scopes called.
|
||||
Assert.Equal(0, sink.Scopes.Count);
|
||||
|
||||
// There are two records for IsEnabled and two for WriteCore.
|
||||
Assert.Equal(4, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(name, enabled.LoggerName);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(name, write.LoggerName);
|
||||
var values = Assert.IsType<RouteConstraintMatcherMatchValues>(write.State);
|
||||
Assert.Equal("RouteConstraintMatcher.Match", values.Name);
|
||||
Assert.Equal("a", values.ConstraintKey);
|
||||
Assert.Equal(constraints["a"], values.Constraint);
|
||||
Assert.Equal(true, values.Matched);
|
||||
|
||||
enabled = sink.Writes[2];
|
||||
Assert.Equal(name, enabled.LoggerName);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
write = sink.Writes[3];
|
||||
Assert.Equal(name, write.LoggerName);
|
||||
values = Assert.IsType<RouteConstraintMatcherMatchValues>(write.State);
|
||||
Assert.Equal("RouteConstraintMatcher.Match", values.Name);
|
||||
Assert.Equal("b", values.ConstraintKey);
|
||||
Assert.Equal(constraints["b"], values.Constraint);
|
||||
Assert.Equal(true, values.Matched);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsTrueOnValidConstraints()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new PassConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstraintsGetTheRightKey()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint("a")},
|
||||
{"b", new PassConstraint("b")}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnInvalidConstraintsThatDontMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new FailConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { c = "value", d = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnInvalidConstraintsThatMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new FailConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnValidAndInvalidConstraintsMixThatMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsTrueOnNullInput()
|
||||
{
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: null,
|
||||
routeValues: new RouteValueDictionary(),
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest,
|
||||
logger: NullLogger.Instance));
|
||||
}
|
||||
#endif
|
||||
|
||||
private class PassConstraint : IRouteConstraint
|
||||
{
|
||||
private readonly string _expectedKey;
|
||||
|
||||
public PassConstraint(string expectedKey = null)
|
||||
{
|
||||
_expectedKey = expectedKey;
|
||||
}
|
||||
|
||||
public bool Match(HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
RouteDirection routeDirection)
|
||||
{
|
||||
if (_expectedKey != null)
|
||||
{
|
||||
Assert.Equal(_expectedKey, routeKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class FailConstraint : IRouteConstraint
|
||||
{
|
||||
public bool Match(HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
RouteDirection routeDirection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if NET45
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Tests
|
||||
{
|
||||
public class ConstraintMatcherTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReturnsTrueOnValidConstraints()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new PassConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConstraintsGetTheRightKey()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint("a")},
|
||||
{"b", new PassConstraint("b")}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnInvalidConstraintsThatDontMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new FailConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { c = "value", d = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnInvalidConstraintsThatMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new FailConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsFalseOnValidAndInvalidConstraintsMixThatMatch()
|
||||
{
|
||||
var constraints = new Dictionary<string, IRouteConstraint>
|
||||
{
|
||||
{"a", new PassConstraint()},
|
||||
{"b", new FailConstraint()}
|
||||
};
|
||||
|
||||
var routeValueDictionary = new RouteValueDictionary(new { a = "value", b = "value" });
|
||||
|
||||
Assert.False(RouteConstraintMatcher.Match(
|
||||
constraints: constraints,
|
||||
routeValues: routeValueDictionary,
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReturnsTrueOnNullInput()
|
||||
{
|
||||
Assert.True(RouteConstraintMatcher.Match(
|
||||
constraints: null,
|
||||
routeValues: new RouteValueDictionary(),
|
||||
httpContext: new Mock<HttpContext>().Object,
|
||||
route: new Mock<IRouter>().Object,
|
||||
routeDirection: RouteDirection.IncomingRequest));
|
||||
}
|
||||
|
||||
private class PassConstraint : IRouteConstraint
|
||||
{
|
||||
private readonly string _expectedKey;
|
||||
|
||||
public PassConstraint(string expectedKey = null)
|
||||
{
|
||||
_expectedKey = expectedKey;
|
||||
}
|
||||
|
||||
public bool Match(HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
RouteDirection routeDirection)
|
||||
{
|
||||
if (_expectedKey != null)
|
||||
{
|
||||
Assert.Equal(_expectedKey, routeKey);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class FailConstraint : IRouteConstraint
|
||||
{
|
||||
public bool Match(HttpContext httpContext,
|
||||
IRouter route,
|
||||
string routeKey,
|
||||
IDictionary<string, object> values,
|
||||
RouteDirection routeDirection)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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
|
||||
{
|
||||
public class BeginScopeContext
|
||||
{
|
||||
public object Scope { get; set; }
|
||||
|
||||
public string LoggerName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.AspNet.Routing
|
||||
{
|
||||
public class NullDisposable : IDisposable
|
||||
{
|
||||
public static NullDisposable Instance = new NullDisposable();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// intentionally does nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class NullLogger : ILogger
|
||||
{
|
||||
public static NullLogger Instance = new NullLogger();
|
||||
|
||||
public IDisposable BeginScope(object state)
|
||||
{
|
||||
return NullDisposable.Instance;
|
||||
}
|
||||
|
||||
public bool WriteCore(TraceType eventType, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class NullLoggerFactory : ILoggerFactory
|
||||
{
|
||||
public static NullLoggerFactory Instance = new NullLoggerFactory();
|
||||
|
||||
public ILogger Create(string name)
|
||||
{
|
||||
return NullLogger.Instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class TestLogger : ILogger
|
||||
{
|
||||
private object _scope;
|
||||
private TestSink _sink;
|
||||
private string _name;
|
||||
|
||||
public TestLogger(string name, TestSink sink)
|
||||
{
|
||||
_sink = sink;
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public IDisposable BeginScope(object state)
|
||||
{
|
||||
_scope = state;
|
||||
|
||||
_sink.Begin(new BeginScopeContext()
|
||||
{
|
||||
LoggerName = _name,
|
||||
Scope = state,
|
||||
});
|
||||
|
||||
return NullDisposable.Instance;
|
||||
}
|
||||
|
||||
public bool WriteCore(TraceType eventType, int eventId, object state, Exception exception, Func<object, Exception, string> formatter)
|
||||
{
|
||||
_sink.Write(new WriteCoreContext()
|
||||
{
|
||||
EventType = eventType,
|
||||
EventId = eventId,
|
||||
State = state,
|
||||
Exception = exception,
|
||||
Formatter = formatter,
|
||||
LoggerName = _name,
|
||||
Scope = _scope
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class TestLoggerFactory : ILoggerFactory
|
||||
{
|
||||
private TestSink _sink;
|
||||
|
||||
public TestLoggerFactory(TestSink sink)
|
||||
{
|
||||
_sink = sink;
|
||||
}
|
||||
|
||||
public ILogger Create(string name)
|
||||
{
|
||||
return new TestLogger(name, _sink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class TestSink
|
||||
{
|
||||
public TestSink(
|
||||
Func<WriteCoreContext, bool> writeEnabled = null,
|
||||
Func<BeginScopeContext, bool> beginEnabled = null)
|
||||
{
|
||||
WriteEnabled = writeEnabled;
|
||||
BeginEnabled = beginEnabled;
|
||||
|
||||
Scopes = new List<BeginScopeContext>();
|
||||
Writes = new List<WriteCoreContext>();
|
||||
}
|
||||
|
||||
public Func<WriteCoreContext, bool> WriteEnabled { get; set; }
|
||||
|
||||
public Func<BeginScopeContext, bool> BeginEnabled { get; set; }
|
||||
|
||||
public List<BeginScopeContext> Scopes { get; set; }
|
||||
|
||||
public List<WriteCoreContext> Writes { get; set; }
|
||||
|
||||
public void Write(WriteCoreContext context)
|
||||
{
|
||||
if (WriteEnabled == null || WriteEnabled(context))
|
||||
{
|
||||
Writes.Add(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void Begin(BeginScopeContext context)
|
||||
{
|
||||
if (BeginEnabled == null || BeginEnabled(context))
|
||||
{
|
||||
Scopes.Add(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool EnableWithTypeName<T>(WriteCoreContext context)
|
||||
{
|
||||
return context.LoggerName.Equals(typeof(T).FullName);
|
||||
}
|
||||
|
||||
public static bool EnableWithTypeName<T>(BeginScopeContext context)
|
||||
{
|
||||
return context.LoggerName.Equals(typeof(T).FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class WriteCoreContext
|
||||
{
|
||||
public TraceType EventType { get; set; }
|
||||
|
||||
public int EventId { get; set; }
|
||||
|
||||
public object State { get; set; }
|
||||
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
public Func<object, Exception, string> Formatter { get; set; }
|
||||
|
||||
public object Scope { get; set; }
|
||||
|
||||
public string LoggerName { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
<Content Include="project.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="ConstraintMatcherTests.cs" />
|
||||
<Compile Include="ConstraintMatcherTest.cs" />
|
||||
<Compile Include="ConstraintsBuilderTests.cs" />
|
||||
<Compile Include="Constraints\ConstraintsTestHelper.cs" />
|
||||
<Compile Include="Constraints\BoolRouteConstraintTests.cs" />
|
||||
|
|
@ -42,15 +42,24 @@
|
|||
<Compile Include="Constraints\IntRouteConstraintsTests.cs" />
|
||||
<Compile Include="Constraints\RequiredRouteConstraintTests.cs" />
|
||||
<Compile Include="DefaultInlineConstraintResolverTest.cs" />
|
||||
<Compile Include="Logging\BeginScopeContext.cs" />
|
||||
<Compile Include="Logging\NullDisposable.cs" />
|
||||
<Compile Include="Logging\NullLogger.cs" />
|
||||
<Compile Include="Logging\NullLoggerFactory.cs" />
|
||||
<Compile Include="Logging\TestLogger.cs" />
|
||||
<Compile Include="Logging\TestLoggerFactory.cs" />
|
||||
<Compile Include="Logging\TestSink.cs" />
|
||||
<Compile Include="Logging\WriteCoreContext.cs" />
|
||||
<Compile Include="RouteCollectionTest.cs" />
|
||||
<Compile Include="InlineRouteParameterParserTests.cs" />
|
||||
<Compile Include="RouteOptionsTests.cs" />
|
||||
<Compile Include="RouterMiddlewareTest.cs" />
|
||||
<Compile Include="RouteValueDictionaryTests.cs" />
|
||||
<Compile Include="TemplateParserDefaultValuesTests.cs" />
|
||||
<Compile Include="Template\TemplateBinderTests.cs" />
|
||||
<Compile Include="Template\TemplateMatcherTests.cs" />
|
||||
<Compile Include="Template\TemplateParserTests.cs" />
|
||||
<Compile Include="Template\TemplateRouteTests.cs" />
|
||||
<Compile Include="Template\TemplateRouteTest.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
</Project>
|
||||
|
|
@ -2,19 +2,102 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#if NET45
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Tests
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class RouteCollectionTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task RouteAsync_LogsCorrectValuesWhenHandled()
|
||||
{
|
||||
// Arrange
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<RouteCollection>,
|
||||
TestSink.EnableWithTypeName<RouteCollection>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var routes = new RouteCollection();
|
||||
var route = CreateRoute(accept: true);
|
||||
routes.Add(route.Object);
|
||||
|
||||
var context = CreateRouteContext("/Cool", loggerFactory);
|
||||
|
||||
// Act
|
||||
await routes.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, scope.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, enabled.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, write.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", write.Scope);
|
||||
var values = Assert.IsType<RouteCollectionRouteAsyncValues>(write.State);
|
||||
Assert.Equal("RouteCollection.RouteAsync", values.Name);
|
||||
Assert.NotNull(values.Routes);
|
||||
Assert.Equal(true, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_LogsCorrectValuesWhenNotHandled()
|
||||
{
|
||||
// Arrange
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<RouteCollection>,
|
||||
TestSink.EnableWithTypeName<RouteCollection>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var routes = new RouteCollection();
|
||||
var route = CreateRoute(accept: false);
|
||||
routes.Add(route.Object);
|
||||
|
||||
var context = CreateRouteContext("/Cool", loggerFactory);
|
||||
|
||||
// Act
|
||||
await routes.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, scope.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, enabled.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(RouteCollection).FullName, write.LoggerName);
|
||||
Assert.Equal("RouteCollection.RouteAsync", write.Scope);
|
||||
var values = Assert.IsType<RouteCollectionRouteAsyncValues>(write.State);
|
||||
Assert.Equal("RouteCollection.RouteAsync", values.Name);
|
||||
Assert.NotNull(values.Routes);
|
||||
Assert.Equal(false, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_FirstMatches()
|
||||
{
|
||||
|
|
@ -214,12 +297,19 @@ namespace Microsoft.AspNet.Routing.Tests
|
|||
return new VirtualPathContext(null, null, null, routeName);
|
||||
}
|
||||
|
||||
private static RouteContext CreateRouteContext(string requestPath)
|
||||
private static RouteContext CreateRouteContext(string requestPath, ILoggerFactory factory = null)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
factory = NullLoggerFactory.Instance;
|
||||
}
|
||||
|
||||
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
||||
request.SetupGet(r => r.Path).Returns(new PathString(requestPath));
|
||||
|
||||
var context = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(factory);
|
||||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||||
|
||||
return new RouteContext(context.Object);
|
||||
|
|
@ -244,5 +334,4 @@ namespace Microsoft.AspNet.Routing.Tests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.Framework.Logging;
|
||||
#if NET45
|
||||
using Moq;
|
||||
#endif
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing
|
||||
{
|
||||
public class RouterMiddlewareTest
|
||||
{
|
||||
#if NET45
|
||||
[Fact]
|
||||
public async void Invoke_LogsCorrectValuesWhenNotHandled()
|
||||
{
|
||||
// Arrange
|
||||
var isHandled = false;
|
||||
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<RouterMiddleware>,
|
||||
TestSink.EnableWithTypeName<RouterMiddleware>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var mockContext = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
mockContext.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(loggerFactory);
|
||||
|
||||
RequestDelegate next = (c) =>
|
||||
{
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
|
||||
var router = new TestRouter(isHandled);
|
||||
var middleware = new RouterMiddleware(next, router);
|
||||
|
||||
// Act
|
||||
await middleware.Invoke(mockContext.Object);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, scope.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, enabled.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, write.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", write.Scope);
|
||||
var values = Assert.IsType<RouterMiddlewareInvokeValues>(write.State);
|
||||
Assert.Equal("RouterMiddleware.Invoke", values.Name);
|
||||
Assert.Equal(false, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async void Invoke_LogsCorrectValuesWhenHandled()
|
||||
{
|
||||
// Arrange
|
||||
var isHandled = true;
|
||||
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<RouterMiddleware>,
|
||||
TestSink.EnableWithTypeName<RouterMiddleware>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var mockContext = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
mockContext.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(loggerFactory);
|
||||
|
||||
RequestDelegate next = (c) =>
|
||||
{
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
|
||||
var router = new TestRouter(isHandled);
|
||||
var middleware = new RouterMiddleware(next, router);
|
||||
|
||||
// Act
|
||||
await middleware.Invoke(mockContext.Object);
|
||||
|
||||
// Assert
|
||||
// exists a BeginScope, verify contents
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, scope.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, enabled.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(RouterMiddleware).FullName, write.LoggerName);
|
||||
Assert.Equal("RouterMiddleware.Invoke", write.Scope);
|
||||
Assert.Equal(typeof(RouterMiddlewareInvokeValues), write.State.GetType());
|
||||
var values = (RouterMiddlewareInvokeValues)write.State;
|
||||
Assert.Equal("RouterMiddleware.Invoke", values.Name);
|
||||
Assert.Equal(true, values.Handled);
|
||||
}
|
||||
#endif
|
||||
|
||||
private class TestRouter : IRouter
|
||||
{
|
||||
private bool _isHandled;
|
||||
|
||||
public TestRouter(bool isHandled)
|
||||
{
|
||||
_isHandled = isHandled;
|
||||
}
|
||||
|
||||
public string GetVirtualPath(VirtualPathContext context)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public Task RouteAsync(RouteContext context)
|
||||
{
|
||||
context.IsHandled = _isHandled;
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,17 +8,158 @@ using System.Linq;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Routing.Constraints;
|
||||
using Microsoft.AspNet.Routing.Logging;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Routing.Template.Tests
|
||||
namespace Microsoft.AspNet.Routing.Template
|
||||
{
|
||||
public class TemplateRouteTests
|
||||
public class TemplateRouteTest
|
||||
{
|
||||
private static IInlineConstraintResolver _inlineConstraintResolver = GetInlineConstraintResolver();
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchSuccess_LogsCorrectValues()
|
||||
{
|
||||
// Arrange
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<TemplateRoute>,
|
||||
TestSink.EnableWithTypeName<TemplateRoute>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var template = "{controller}/{action}";
|
||||
|
||||
var route = CreateRoute(template);
|
||||
var context = CreateRouteContext("/Home/Index", loggerFactory);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, scope.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, enabled.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, write.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", write.Scope);
|
||||
|
||||
// verify WriteCore state contents
|
||||
var values = Assert.IsType<TemplateRouteRouteAsyncValues>(write.State);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", values.Name);
|
||||
Assert.Equal("Home/Index", values.RequestPath);
|
||||
Assert.Equal(template, values.Template);
|
||||
Assert.NotNull(values.DefaultValues);
|
||||
Assert.NotNull(values.ProducedValues);
|
||||
Assert.Equal(true, values.MatchedTemplate);
|
||||
Assert.Equal(true, values.MatchedConstraints);
|
||||
Assert.Equal(true, values.Matched);
|
||||
Assert.Equal(context.IsHandled, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchFailOnValues_LogsCorrectValues()
|
||||
{
|
||||
// Arrange
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<TemplateRoute>,
|
||||
TestSink.EnableWithTypeName<TemplateRoute>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var template = "{controller}/{action}";
|
||||
|
||||
var route = CreateRoute(template);
|
||||
var context = CreateRouteContext("/Home/Index/Failure", loggerFactory);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, scope.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, enabled.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, write.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", write.Scope);
|
||||
var values = Assert.IsType<TemplateRouteRouteAsyncValues>(write.State);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", values.Name);
|
||||
Assert.Equal("Home/Index/Failure", values.RequestPath);
|
||||
Assert.Equal(template, values.Template);
|
||||
Assert.NotNull(values.DefaultValues);
|
||||
Assert.Null(values.ProducedValues);
|
||||
Assert.Equal(false, values.MatchedTemplate);
|
||||
Assert.Equal(false, values.MatchedConstraints);
|
||||
Assert.Equal(false, values.Matched);
|
||||
Assert.Equal(context.IsHandled, values.Handled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RouteAsync_MatchFailOnConstraints_LogsCorrectValues()
|
||||
{
|
||||
// Arrange
|
||||
var sink = new TestSink(
|
||||
TestSink.EnableWithTypeName<TemplateRoute>,
|
||||
TestSink.EnableWithTypeName<TemplateRoute>);
|
||||
var loggerFactory = new TestLoggerFactory(sink);
|
||||
|
||||
var template = "{controller}/{action}/{id:int}";
|
||||
|
||||
var route = CreateRoute(template);
|
||||
var context = CreateRouteContext("/Home/Index/Failure", loggerFactory);
|
||||
|
||||
// Act
|
||||
await route.RouteAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(1, sink.Scopes.Count);
|
||||
var scope = sink.Scopes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, scope.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", scope.Scope);
|
||||
|
||||
// There is a record for IsEnabled and one for WriteCore.
|
||||
Assert.Equal(2, sink.Writes.Count);
|
||||
|
||||
var enabled = sink.Writes[0];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, enabled.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", enabled.Scope);
|
||||
Assert.Null(enabled.State);
|
||||
|
||||
var write = sink.Writes[1];
|
||||
Assert.Equal(typeof(TemplateRoute).FullName, write.LoggerName);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", write.Scope);
|
||||
var values = Assert.IsType<TemplateRouteRouteAsyncValues>(write.State);
|
||||
Assert.Equal("TemplateRoute.RouteAsync", values.Name);
|
||||
Assert.Equal("Home/Index/Failure", values.RequestPath);
|
||||
Assert.Equal(template, values.Template);
|
||||
Assert.NotNull(values.DefaultValues);
|
||||
Assert.NotNull(values.ProducedValues);
|
||||
Assert.Equal(true, values.MatchedTemplate);
|
||||
Assert.Equal(false, values.MatchedConstraints);
|
||||
Assert.Equal(false, values.Matched);
|
||||
Assert.Equal(context.IsHandled, values.Handled);
|
||||
}
|
||||
|
||||
#region Route Matching
|
||||
|
||||
// PathString in HttpAbstractions guarantees a leading slash - so no value in testing other cases.
|
||||
|
|
@ -116,20 +257,26 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
Assert.Null(context.RouteData.Values["1controller"]);
|
||||
}
|
||||
|
||||
private static RouteContext CreateRouteContext(string requestPath)
|
||||
private static RouteContext CreateRouteContext(string requestPath, ILoggerFactory factory = null)
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
factory = NullLoggerFactory.Instance;
|
||||
}
|
||||
|
||||
var request = new Mock<HttpRequest>(MockBehavior.Strict);
|
||||
request.SetupGet(r => r.Path).Returns(new PathString(requestPath));
|
||||
|
||||
var context = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(factory);
|
||||
context.SetupGet(c => c.Request).Returns(request.Object);
|
||||
|
||||
return new RouteContext(context.Object);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Route Binding
|
||||
#region Route Binding
|
||||
|
||||
[Fact]
|
||||
public void GetVirtualPath_Success()
|
||||
|
|
@ -502,6 +649,8 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
private static VirtualPathContext CreateVirtualPathContext(IDictionary<string, object> values, IDictionary<string, object> ambientValues)
|
||||
{
|
||||
var context = new Mock<HttpContext>(MockBehavior.Strict);
|
||||
context.Setup(m => m.RequestServices.GetService(typeof(ILoggerFactory)))
|
||||
.Returns(NullLoggerFactory.Instance);
|
||||
|
||||
return new VirtualPathContext(context.Object, ambientValues, values);
|
||||
}
|
||||
|
|
@ -712,5 +861,4 @@ namespace Microsoft.AspNet.Routing.Template.Tests
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
"Microsoft.AspNet.Http" : "1.0.0-*",
|
||||
"Microsoft.AspNet.Routing" : "",
|
||||
"Microsoft.AspNet.Testing" : "1.0.0-*",
|
||||
"Microsoft.Framework.Logging" : "1.0.0-*",
|
||||
"Xunit.KRunner": "1.0.0-*"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue