This commit is contained in:
Yishai Galatzer 2014-03-26 18:57:44 -07:00
Родитель cf16d6cba7
Коммит a917cbd577
16 изменённых файлов: 238 добавлений и 54 удалений

Просмотреть файл

@ -1,21 +1,22 @@
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
namespace RoutingSample.Web
{
public class HttpContextRouteEndpoint : IRouter
public class DelegateRouteEndpoint : IRouter
{
private readonly RequestDelegate _appFunc;
public delegate Task RoutedDelegate(RouteContext context);
public HttpContextRouteEndpoint(RequestDelegate appFunc)
private readonly RoutedDelegate _appFunc;
public DelegateRouteEndpoint(RoutedDelegate appFunc)
{
_appFunc = appFunc;
}
public async Task RouteAsync(RouteContext context)
{
await _appFunc(context.HttpContext);
await _appFunc(context);
context.IsHandled = true;
}

Просмотреть файл

@ -0,0 +1,15 @@
using System.Collections.Generic;
using System.Linq;
namespace RoutingSample.Web
{
public static class DictionaryExtensions
{
public static string Print(this IDictionary<string, object> routeValues)
{
var values = routeValues.Select(kvp => kvp.Key + ":" + kvp.Value.ToString());
return string.Join(" ", values);
}
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using Microsoft.AspNet.Abstractions;
using System.Text.RegularExpressions;
using Microsoft.AspNet.Abstractions;
using Microsoft.AspNet.Routing;
namespace RoutingSample.Web
@ -9,11 +10,23 @@ namespace RoutingSample.Web
{
var routes = builder.UseRouter();
var endpoint1 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("match1"));
var endpoint2 = new HttpContextRouteEndpoint(async (context) => await context.Response.WriteAsync("Hello, World!"));
var endpoint1 = new DelegateRouteEndpoint(async (context) =>
await context.HttpContext.Response.WriteAsync(
"match1, route values -" + context.Values.Print()));
var endpoint2 = new DelegateRouteEndpoint(async (context) =>
await context.HttpContext.Response.WriteAsync("Hello, World!"));
routes.DefaultHandler = endpoint1;
routes.AddPrefixRoute("api/store");
routes.MapRoute("api/constraint/{controller}", null, new { controller = "my.*" });
routes.MapRoute("api/rconstraint/{controller}",
new { foo = "Bar" },
new { controller = new RegexConstraint("^(my.*)$") });
routes.MapRoute("api/r2constraint/{controller}",
new { foo = "Bar2" },
new { controller = new RegexConstraint(new Regex("^(my.*)$")) });
routes.MapRoute("api/{controller}/{*extra}", new { controller = "Store" });
routes.AddPrefixRoute("hello/world", endpoint2);

Просмотреть файл

@ -0,0 +1,14 @@
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public interface IRouteConstraint
{
bool Match([NotNull] HttpContext httpContext,
[NotNull] IRouter route,
[NotNull] string routeKey,
[NotNull] IDictionary<string, object> values,
RouteDirection routeDirection);
}
}

Просмотреть файл

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
public interface IRouteValues
{
IDictionary<string, object> Values
{
get;
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
using System;
namespace Microsoft.AspNet.Routing
{
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false)]
internal sealed class NotNullAttribute : Attribute
{
}
}

Просмотреть файл

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public class RegexConstraint : IRouteConstraint
{
public RegexConstraint([NotNull] Regex regex)
{
Constraint = regex;
}
public RegexConstraint([NotNull] string regexPattern)
{
Constraint = new Regex(regexPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
}
public Regex Constraint { get; private set; }
public bool Match([NotNull] HttpContext httpContext,
[NotNull] IRouter route,
[NotNull] string routeKey,
[NotNull] IDictionary<string, object> routeValues,
RouteDirection routeDirection)
{
object routeValue;
if (routeValues.TryGetValue(routeKey, out routeValue)
&& routeValue != null)
{
var parameterValueString = Convert.ToString(routeValue, CultureInfo.InvariantCulture);
return Constraint.IsMatch(parameterValueString);
}
return false;
}
}
}

Просмотреть файл

@ -1,5 +1,4 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;

Просмотреть файл

@ -27,7 +27,39 @@ namespace Microsoft.AspNet.Routing
throw new ArgumentException("DefaultHandler must be set.");
}
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults));
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults, null));
return routes;
}
public static IRouteCollection MapRoute(this IRouteCollection routes, string template, object defaults, object constraints)
{
MapRoute(routes, template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints));
return routes;
}
public static IRouteCollection MapRoute(this IRouteCollection routes, string template, object defaults,
IDictionary<string, object> constraints)
{
MapRoute(routes, template, new RouteValueDictionary(defaults), constraints);
return routes;
}
public static IRouteCollection MapRoute(this IRouteCollection routes, string template,
IDictionary<string, object> defaults, object constraints)
{
MapRoute(routes, template, defaults, new RouteValueDictionary(constraints));
return routes;
}
public static IRouteCollection MapRoute(this IRouteCollection routes, string template,
IDictionary<string, object> defaults, IDictionary<string, object> constraints)
{
if (routes.DefaultHandler == null)
{
throw new ArgumentException("DefaultHandler must be set.");
}
routes.Add(new TemplateRoute(routes.DefaultHandler, template, defaults, constraints));
return routes;
}
}

Просмотреть файл

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
public class RouteConstraintBuilder
{
public static IDictionary<string, IRouteConstraint>
BuildConstraints(IDictionary<string, object> inputConstraints)
{
if (inputConstraints == null || inputConstraints.Count == 0)
{
return null;
}
var constraints = new Dictionary<string, IRouteConstraint>(inputConstraints.Count, StringComparer.OrdinalIgnoreCase);
foreach (var kvp in inputConstraints)
{
var constraint = kvp.Value as IRouteConstraint;
if (constraint == null)
{
var regexPattern = kvp.Value as string;
if (regexPattern == null)
{
throw new InvalidOperationException("Constraint can be a valid regex string or an IRouteConstraint");
}
var constraintsRegEx = "^(" + regexPattern + ")$";
constraint = new RegexConstraint(constraintsRegEx);
}
constraints.Add(kvp.Key, constraint);
}
return constraints;
}
}
}

Просмотреть файл

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing
{
public static class RouteConstraintMatcher
{
public static bool Match([NotNull] IDictionary<string, IRouteConstraint> constraints,
[NotNull] IDictionary<string, object> routeValues,
[NotNull] HttpContext httpContext,
[NotNull] IRouter route,
[NotNull] RouteDirection routeDirection)
{
if (constraints == null)
{
return true;
}
foreach (var kvp in constraints)
{
var constraint = kvp.Value;
if (!constraint.Match(httpContext, route, kvp.Key, routeValues, routeDirection))
{
return false;
}
}
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,8 @@
namespace Microsoft.AspNet.Routing
{
public enum RouteDirection
{
IncomingRequest,
UrlGeneration,
}
}

Просмотреть файл

@ -1,20 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
using System.Collections.Generic;
namespace Microsoft.AspNet.Routing
{
public class RouteValues : IRouteValues
{
public RouteValues(IDictionary<string, object> values)
{
Values = values;
}
public IDictionary<string, object> Values
{
get;
private set;
}
}
}

Просмотреть файл

@ -2,27 +2,26 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading.Tasks;
using Microsoft.AspNet.Abstractions;
namespace Microsoft.AspNet.Routing.Template
{
public class TemplateRoute : IRouter
{
private readonly IDictionary<string, object> _defaults;
private readonly IDictionary<string, IRouteConstraint> _constraints;
private readonly IRouter _target;
private readonly Template _parsedTemplate;
private readonly string _routeTemplate;
private readonly TemplateMatcher _matcher;
private readonly TemplateBinder _binder;
public TemplateRoute(IRouter target, string routeTemplate)
: this(target, routeTemplate, null)
: this(target, routeTemplate, null, null)
{
}
public TemplateRoute(IRouter target, string routeTemplate, IDictionary<string, object> defaults)
public TemplateRoute(IRouter target, string routeTemplate, IDictionary<string, object> defaults,
IDictionary<string, object> constraints)
{
if (target == null)
{
@ -32,11 +31,12 @@ namespace Microsoft.AspNet.Routing.Template
_target = target;
_routeTemplate = routeTemplate ?? string.Empty;
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
_constraints = RouteConstraintBuilder.BuildConstraints(constraints);
// The parser will throw for invalid routes.
_parsedTemplate = TemplateParser.Parse(RouteTemplate);
var parsedTemplate = TemplateParser.Parse(RouteTemplate);
_matcher = new TemplateMatcher(_parsedTemplate);
_matcher = new TemplateMatcher(parsedTemplate);
_binder = new TemplateBinder(_parsedTemplate, _defaults);
}
@ -74,9 +74,16 @@ namespace Microsoft.AspNet.Routing.Template
// Not currently doing anything to clean this up if it's not a match. Consider hardening this.
context.Values = values;
if (RouteConstraintMatcher.Match(_constraints,
values,
context.HttpContext,
this,
RouteDirection.IncomingRequest))
{
await _target.RouteAsync(context);
}
}
}
public string GetVirtualPath(VirtualPathContext context)
{

Просмотреть файл

@ -1,7 +1,8 @@
{
"version": "0.1-alpha-*",
"dependencies": {
"Microsoft.AspNet.Abstractions": "0.1-alpha-*"
"Microsoft.AspNet.Abstractions": "0.1-alpha-*",
"Microsoft.AspNet.DependencyInjection" : "0.1-alpha-*"
},
"configurations": {
"net45": {},
@ -19,6 +20,9 @@
"System.Runtime": "4.0.20.0",
"System.Runtime.Extensions": "4.0.10.0",
"System.Text.RegularExpressions": "4.0.0.0",
"System.Runtime.Hosting": "3.9.0.0",
"System.Runtime.InteropServices": "4.0.10.0",
"System.Text.Encoding": "4.0.10.0",
"System.Threading.Tasks": "4.0.0.0"
}
}

Просмотреть файл

@ -196,7 +196,7 @@ namespace Microsoft.AspNet.Routing.Template.Tests
private static TemplateRoute CreateRoute(string template, object defaults, bool accept = true)
{
return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults));
return new TemplateRoute(CreateTarget(accept), template, new RouteValueDictionary(defaults), null);
}
private static IRouter CreateTarget(bool accept = true)