diff --git a/src/Microsoft.AspNet.Routing/Logging/LogFormatter.cs b/src/Microsoft.AspNet.Routing/Logging/LogFormatter.cs
new file mode 100644
index 0000000000..63971503df
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/LogFormatter.cs
@@ -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
+ {
+ ///
+ /// A formatter for use with .
+ ///
+ 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 "";
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/LoggerExtensions.cs b/src/Microsoft.AspNet.Routing/Logging/LoggerExtensions.cs
new file mode 100644
index 0000000000..ebcdca6744
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/LoggerExtensions.cs
@@ -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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/RouteCollectionRouteAsyncValues.cs b/src/Microsoft.AspNet.Routing/Logging/RouteCollectionRouteAsyncValues.cs
new file mode 100644
index 0000000000..f8303bb91a
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/RouteCollectionRouteAsyncValues.cs
@@ -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
+{
+ ///
+ /// Describes the state of
+ /// .
+ ///
+ public class RouteCollectionRouteAsyncValues
+ {
+ ///
+ /// The name of the state.
+ ///
+ public string Name
+ {
+ get
+ {
+ return "RouteCollection.RouteAsync";
+ }
+ }
+
+ ///
+ /// The available routes.
+ ///
+ public IList Routes { get; set; }
+
+ ///
+ /// True if the request is handled.
+ ///
+ public bool Handled { get; set; }
+
+ ///
+ /// A summary of the data for display.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ return Summary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/RouteConstraintMatcherMatchValues.cs b/src/Microsoft.AspNet.Routing/Logging/RouteConstraintMatcherMatchValues.cs
new file mode 100644
index 0000000000..4f6272eb70
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/RouteConstraintMatcherMatchValues.cs
@@ -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
+{
+ ///
+ /// Describes the state of .
+ ///
+ public class RouteConstraintMatcherMatchValues
+ {
+ ///
+ /// The name of the state.
+ ///
+ public string Name
+ {
+ get
+ {
+ return "RouteConstraintMatcher.Match";
+ }
+ }
+
+ ///
+ /// The key of the constraint.
+ ///
+ public string ConstraintKey { get; set; }
+
+ ///
+ /// The constraint.
+ ///
+ public IRouteConstraint Constraint { get; set; }
+
+ ///
+ /// True if the matched.
+ ///
+ public bool Matched { get; set; }
+
+ ///
+ /// A summary of the data for display.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ return Summary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/RouterMiddlewareInvokeValues.cs b/src/Microsoft.AspNet.Routing/Logging/RouterMiddlewareInvokeValues.cs
new file mode 100644
index 0000000000..7a474b1a9d
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/RouterMiddlewareInvokeValues.cs
@@ -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
+{
+ ///
+ /// Describes the state of
+ /// .
+ ///
+ public class RouterMiddlewareInvokeValues
+ {
+ ///
+ /// The name of the state.
+ ///
+ public string Name
+ {
+ get
+ {
+ return "RouterMiddleware.Invoke";
+ }
+ }
+
+ ///
+ /// True if the request is handled.
+ ///
+ public bool Handled { get; set; }
+
+ ///
+ /// A summary of the data for display.
+ ///
+ public string Summary
+ {
+ get
+ {
+ var builder = new StringBuilder();
+ builder.AppendLine(Name);
+ builder.Append("\tHandled? ");
+ builder.Append(Handled);
+ return builder.ToString();
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ return Summary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/StringBuilderHelpers.cs b/src/Microsoft.AspNet.Routing/Logging/StringBuilderHelpers.cs
new file mode 100644
index 0000000000..218ff36f71
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/StringBuilderHelpers.cs
@@ -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(StringBuilder builder, IEnumerable 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(StringBuilder builder, IDictionary 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");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Logging/TemplateRouteRouteAsyncValues.cs b/src/Microsoft.AspNet.Routing/Logging/TemplateRouteRouteAsyncValues.cs
new file mode 100644
index 0000000000..4163f88391
--- /dev/null
+++ b/src/Microsoft.AspNet.Routing/Logging/TemplateRouteRouteAsyncValues.cs
@@ -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
+{
+ ///
+ /// Describes the state of
+ /// .
+ ///
+ public class TemplateRouteRouteAsyncValues
+ {
+ ///
+ /// The name of the state.
+ ///
+ public string Name
+ {
+ get
+ {
+ return "TemplateRoute.RouteAsync";
+ }
+ }
+
+ ///
+ /// The target.
+ ///
+ public IRouter Target { get; set; }
+
+ ///
+ /// The template.
+ ///
+ public string Template { get; set; }
+
+ ///
+ /// The request path.
+ ///
+ public string RequestPath { get; set; }
+
+ ///
+ /// The values produced by default.
+ ///
+ public IDictionary DefaultValues { get; set; }
+
+ ///
+ /// The values produced from the request.
+ ///
+ public IDictionary ProducedValues { get; set; }
+
+ ///
+ /// The constraints matched on the produced values.
+ ///
+ public IDictionary Constraints { get; set; }
+
+ ///
+ /// True if the matched.
+ ///
+ public bool MatchedTemplate { get; set; }
+
+ ///
+ /// True if the matched.
+ ///
+ public bool MatchedConstraints { get; set; }
+
+ ///
+ /// True if this route matched.
+ ///
+ public bool Matched { get; set; }
+
+ ///
+ /// True if the request is handled.
+ ///
+ public bool Handled { get; set; }
+
+ ///
+ /// A summary of the data for display.
+ ///
+ 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();
+ }
+ }
+
+ ///
+ public override string ToString()
+ {
+ return Summary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj b/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj
index 6b0e870438..2168cb71e0 100644
--- a/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj
+++ b/src/Microsoft.AspNet.Routing/Microsoft.AspNet.Routing.kproj
@@ -47,10 +47,17 @@
+
+
+
+
+
+
+
diff --git a/src/Microsoft.AspNet.Routing/RouteCollection.cs b/src/Microsoft.AspNet.Routing/RouteCollection.cs
index 682a96649b..e232347929 100644
--- a/src/Microsoft.AspNet.Routing/RouteCollection.cs
+++ b/src/Microsoft.AspNet.Routing/RouteCollection.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 _routes = new List();
private readonly List _unnamedRoutes = new List();
- private readonly Dictionary _namedRoutes =
+ private readonly Dictionary _namedRoutes =
new Dictionary(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();
+ _logger = factory.Create();
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNet.Routing/RouteConstraintMatcher.cs b/src/Microsoft.AspNet.Routing/RouteConstraintMatcher.cs
index 64c3341e79..c85f934fd0 100644
--- a/src/Microsoft.AspNet.Routing/RouteConstraintMatcher.cs
+++ b/src/Microsoft.AspNet.Routing/RouteConstraintMatcher.cs
@@ -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 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;
diff --git a/src/Microsoft.AspNet.Routing/RouterMiddleware.cs b/src/Microsoft.AspNet.Routing/RouterMiddleware.cs
index 50e609b609..c249537dca 100644
--- a/src/Microsoft.AspNet.Routing/RouterMiddleware.cs
+++ b/src/Microsoft.AspNet.Routing/RouterMiddleware.cs
@@ -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();
+ _logger = factory.Create();
}
}
}
diff --git a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
index e6c774789d..6a73d0d97f 100644
--- a/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
+++ b/src/Microsoft.AspNet.Routing/Template/TemplateRoute.cs
@@ -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 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();
+ _logger = factory.Create();
+ _constraintLogger = factory.Create(typeof(RouteConstraintMatcher).FullName);
+ }
+ }
+
+ public override string ToString()
+ {
+ return _routeTemplate;
+ }
}
}
diff --git a/src/Microsoft.AspNet.Routing/project.json b/src/Microsoft.AspNet.Routing/project.json
index 196d83ee36..24690c6e95 100644
--- a/src/Microsoft.AspNet.Routing/project.json
+++ b/src/Microsoft.AspNet.Routing/project.json
@@ -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": {
diff --git a/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTest.cs b/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTest.cs
new file mode 100644
index 0000000000..3a88c816dc
--- /dev/null
+++ b/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTest.cs
@@ -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
+ {
+ {"a", new PassConstraint()},
+ {"b", new FailConstraint()}
+ };
+
+ // Act
+ RouteConstraintMatcher.Match(
+ constraints: constraints,
+ routeValues: routeValueDictionary,
+ httpContext: new Mock().Object,
+ route: new Mock().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
+ {
+ {"a", new PassConstraint()},
+ {"b", new FailConstraint()}
+ };
+
+ // Act
+ RouteConstraintMatcher.Match(
+ constraints: constraints,
+ routeValues: routeValueDictionary,
+ httpContext: new Mock().Object,
+ route: new Mock().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(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(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
+ {
+ {"a", new PassConstraint()},
+ {"b", new PassConstraint()}
+ };
+
+ // Act
+ RouteConstraintMatcher.Match(
+ constraints: constraints,
+ routeValues: routeValueDictionary,
+ httpContext: new Mock().Object,
+ route: new Mock().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(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(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
+ {
+ {"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().Object,
+ route: new Mock().Object,
+ routeDirection: RouteDirection.IncomingRequest,
+ logger: NullLogger.Instance));
+ }
+
+ [Fact]
+ public void ConstraintsGetTheRightKey()
+ {
+ var constraints = new Dictionary
+ {
+ {"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().Object,
+ route: new Mock().Object,
+ routeDirection: RouteDirection.IncomingRequest,
+ logger: NullLogger.Instance));
+ }
+
+ [Fact]
+ public void ReturnsFalseOnInvalidConstraintsThatDontMatch()
+ {
+ var constraints = new Dictionary
+ {
+ {"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().Object,
+ route: new Mock().Object,
+ routeDirection: RouteDirection.IncomingRequest,
+ logger: NullLogger.Instance));
+ }
+
+ [Fact]
+ public void ReturnsFalseOnInvalidConstraintsThatMatch()
+ {
+ var constraints = new Dictionary
+ {
+ {"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().Object,
+ route: new Mock().Object,
+ routeDirection: RouteDirection.IncomingRequest,
+ logger: NullLogger.Instance));
+ }
+
+ [Fact]
+ public void ReturnsFalseOnValidAndInvalidConstraintsMixThatMatch()
+ {
+ var constraints = new Dictionary
+ {
+ {"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().Object,
+ route: new Mock().Object,
+ routeDirection: RouteDirection.IncomingRequest,
+ logger: NullLogger.Instance));
+ }
+
+ [Fact]
+ public void ReturnsTrueOnNullInput()
+ {
+ Assert.True(RouteConstraintMatcher.Match(
+ constraints: null,
+ routeValues: new RouteValueDictionary(),
+ httpContext: new Mock().Object,
+ route: new Mock().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 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 values,
+ RouteDirection routeDirection)
+ {
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs b/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs
deleted file mode 100644
index 0a3ff5926b..0000000000
--- a/test/Microsoft.AspNet.Routing.Tests/ConstraintMatcherTests.cs
+++ /dev/null
@@ -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
- {
- {"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().Object,
- route: new Mock().Object,
- routeDirection: RouteDirection.IncomingRequest));
- }
-
- [Fact]
- public void ConstraintsGetTheRightKey()
- {
- var constraints = new Dictionary
- {
- {"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().Object,
- route: new Mock().Object,
- routeDirection: RouteDirection.IncomingRequest));
- }
-
- [Fact]
- public void ReturnsFalseOnInvalidConstraintsThatDontMatch()
- {
- var constraints = new Dictionary
- {
- {"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().Object,
- route: new Mock().Object,
- routeDirection: RouteDirection.IncomingRequest));
- }
-
- [Fact]
- public void ReturnsFalseOnInvalidConstraintsThatMatch()
- {
- var constraints = new Dictionary
- {
- {"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().Object,
- route: new Mock().Object,
- routeDirection: RouteDirection.IncomingRequest));
- }
-
- [Fact]
- public void ReturnsFalseOnValidAndInvalidConstraintsMixThatMatch()
- {
- var constraints = new Dictionary
- {
- {"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().Object,
- route: new Mock().Object,
- routeDirection: RouteDirection.IncomingRequest));
- }
-
- [Fact]
- public void ReturnsTrueOnNullInput()
- {
- Assert.True(RouteConstraintMatcher.Match(
- constraints: null,
- routeValues: new RouteValueDictionary(),
- httpContext: new Mock().Object,
- route: new Mock().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 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 values,
- RouteDirection routeDirection)
- {
- return false;
- }
- }
- }
-}
-#endif
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Routing.Tests/Logging/BeginScopeContext.cs b/test/Microsoft.AspNet.Routing.Tests/Logging/BeginScopeContext.cs
new file mode 100644
index 0000000000..456d1c1a6d
--- /dev/null
+++ b/test/Microsoft.AspNet.Routing.Tests/Logging/BeginScopeContext.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Routing.Tests/Logging/NullDisposable.cs b/test/Microsoft.AspNet.Routing.Tests/Logging/NullDisposable.cs
new file mode 100644
index 0000000000..ad687d1dbe
--- /dev/null
+++ b/test/Microsoft.AspNet.Routing.Tests/Logging/NullDisposable.cs
@@ -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
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNet.Routing.Tests/Logging/NullLogger.cs b/test/Microsoft.AspNet.Routing.Tests/Logging/NullLogger.cs
new file mode 100644
index 0000000000..f92dc40c34
--- /dev/null
+++ b/test/Microsoft.AspNet.Routing.Tests/Logging/NullLogger.cs
@@ -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