using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using EmbedIO.Internal;
using EmbedIO.Utilities;
using Swan.Configuration;
namespace EmbedIO.Routing
{
///
/// Implements the logic for resolving the requested path of a HTTP context against a route,
/// possibly handling different contexts via different handlers.
///
/// The type of the data used to select a suitable handler
/// for the context.
///
public abstract class RouteResolverBase : ConfiguredObject
{
private readonly List<(TData data, RouteHandlerCallback handler)> _dataHandlerPairs
= new List<(TData data, RouteHandlerCallback handler)>();
///
/// Initializes a new instance of the class.
///
/// The to match URL paths against.
///
/// is .
///
protected RouteResolverBase(RouteMatcher matcher)
{
Matcher = Validate.NotNull(nameof(matcher), matcher);
}
///
/// Gets the used to match routes.
///
public RouteMatcher Matcher { get; }
///
/// Gets the route this resolver matches URL paths against.
///
public string Route => Matcher.Route;
///
/// Gets a value indicating whether is a base route.
///
public bool IsBaseRoute => Matcher.IsBaseRoute;
///
/// Associates some data to a handler.
/// The method calls
/// to extract data from the context; then, for each registered data / handler pair,
/// is called to determine whether
/// should be called.
///
/// Data used to determine which contexts are
/// suitable to be handled by .
/// A callback used to handle matching contexts.
/// is .
///
///
///
///
public void Add(TData data, RouteHandlerCallback handler)
{
EnsureConfigurationNotLocked();
handler = Validate.NotNull(nameof(handler), handler);
_dataHandlerPairs.Add((data, handler));
}
///
/// Associates some data to a synchronous handler.
/// The method calls
/// to extract data from the context; then, for each registered data / handler pair,
/// is called to determine whether
/// should be called.
///
/// Data used to determine which contexts are
/// suitable to be handled by .
/// A callback used to handle matching contexts.
/// is .
///
///
///
///
public void Add(TData data, SyncRouteHandlerCallback handler)
{
EnsureConfigurationNotLocked();
handler = Validate.NotNull(nameof(handler), handler);
_dataHandlerPairs.Add((data, (ctx, route) => {
handler(ctx, route);
return Task.CompletedTask;
}));
}
///
/// Locks this instance, preventing further handler additions.
///
public void Lock() => LockConfiguration();
///
/// Asynchronously matches a URL path against ;
/// if the match is successful, tries to handle the specified
/// using handlers selected according to data extracted from the context.
/// Registered data / handler pairs are tried in the same order they were added.
///
/// The context to handle.
/// A , representing the ongoing operation,
/// that will return a result in the form of one of the constants.
///
///
///
///
public async Task ResolveAsync(IHttpContext context)
{
LockConfiguration();
var match = Matcher.Match(context.RequestedPath);
if (match == null)
return RouteResolutionResult.RouteNotMatched;
var contextData = GetContextData(context);
var result = RouteResolutionResult.NoHandlerSelected;
foreach (var (data, handler) in _dataHandlerPairs)
{
if (!MatchContextData(contextData, data))
continue;
try
{
await handler(context, match).ConfigureAwait(false);
return RouteResolutionResult.Success;
}
catch (RequestHandlerPassThroughException)
{
result = RouteResolutionResult.NoHandlerSuccessful;
}
}
return result;
}
///
/// Called by to extract data from a context.
/// The extracted data are then used to select which handlers are suitable
/// to handle the context.
///
/// The HTTP context to extract data from.
/// The extracted data.
///
///
protected abstract TData GetContextData(IHttpContext context);
///
/// Called by to match data extracted from a context
/// against data associated with a handler.
///
/// The data extracted from the context.
/// The data associated with the handler.
/// if the handler should be called to handle the context;
/// otherwise, .
protected abstract bool MatchContextData(TData contextData, TData handlerData);
}
}