110 lines
3.8 KiB
C#
110 lines
3.8 KiB
C#
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading.Tasks;
|
|
|
|
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 string _routeTemplate;
|
|
private readonly TemplateMatcher _matcher;
|
|
private readonly TemplateBinder _binder;
|
|
|
|
public TemplateRoute(IRouter target, string routeTemplate)
|
|
: this(target, routeTemplate, null, null)
|
|
{
|
|
}
|
|
|
|
public TemplateRoute([NotNull] IRouter target, string routeTemplate, IDictionary<string, object> defaults,
|
|
IDictionary<string, object> constraints)
|
|
{
|
|
_target = target;
|
|
_routeTemplate = routeTemplate ?? string.Empty;
|
|
_defaults = defaults ?? new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
|
|
_constraints = RouteConstraintBuilder.BuildConstraints(constraints, _routeTemplate);
|
|
|
|
// The parser will throw for invalid routes.
|
|
var parsedTemplate = TemplateParser.Parse(RouteTemplate);
|
|
|
|
_matcher = new TemplateMatcher(parsedTemplate);
|
|
_binder = new TemplateBinder(_parsedTemplate, _defaults);
|
|
}
|
|
|
|
public IDictionary<string, object> Defaults
|
|
{
|
|
get { return _defaults; }
|
|
}
|
|
|
|
public string RouteTemplate
|
|
{
|
|
get { return _routeTemplate; }
|
|
}
|
|
|
|
public async virtual Task RouteAsync([NotNull] RouteContext context)
|
|
{
|
|
var requestPath = context.RequestPath;
|
|
if (!string.IsNullOrEmpty(requestPath) && requestPath[0] == '/')
|
|
{
|
|
requestPath = requestPath.Substring(1);
|
|
}
|
|
|
|
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.Values = values;
|
|
|
|
if (RouteConstraintMatcher.Match(_constraints,
|
|
values,
|
|
context.HttpContext,
|
|
this,
|
|
RouteDirection.IncomingRequest))
|
|
{
|
|
await _target.RouteAsync(context);
|
|
}
|
|
}
|
|
}
|
|
|
|
public string GetVirtualPath(VirtualPathContext context)
|
|
{
|
|
var values = _binder.GetAcceptedValues(context.AmbientValues, context.Values);
|
|
if (values == null)
|
|
{
|
|
// We're missing one the required values for this route.
|
|
return null;
|
|
}
|
|
|
|
// Validate that the target can accept these values.
|
|
var path = _target.GetVirtualPath(context);
|
|
if (path != null)
|
|
{
|
|
// If the target generates a value then that can short circuit.
|
|
return path;
|
|
}
|
|
else if (!context.IsBound)
|
|
{
|
|
// The target has rejected these values.
|
|
return null;
|
|
}
|
|
|
|
path = _binder.BindValues(values);
|
|
if (path == null)
|
|
{
|
|
context.IsBound = false;
|
|
}
|
|
|
|
return path;
|
|
}
|
|
}
|
|
}
|