using System; using System.Collections.Generic; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.SpaServices; // Putting in this namespace so it's always available whenever MapRoute is namespace Microsoft.AspNetCore.Builder { /// /// Extension methods useful for configuring routing in a single-page application (SPA). /// public static class SpaRouteExtensions { private const string ClientRouteTokenName = "clientRoute"; /// /// Configures a route that is automatically bypassed if the requested URL appears to be for a static file /// (e.g., if it has a filename extension). /// /// The . /// The route name. /// Default route parameters. /// Route constraints. /// Route data tokens. public static void MapSpaFallbackRoute( this IRouteBuilder routeBuilder, string name, object defaults, object constraints = null, object dataTokens = null) { MapSpaFallbackRoute( routeBuilder, name, /* templatePrefix */ null, defaults, constraints, dataTokens); } /// /// Configures a route that is automatically bypassed if the requested URL appears to be for a static file /// (e.g., if it has a filename extension). /// /// The . /// The route name. /// The template prefix. /// Default route parameters. /// Route constraints. /// Route data tokens. public static void MapSpaFallbackRoute( this IRouteBuilder routeBuilder, string name, string templatePrefix, object defaults, object constraints = null, object dataTokens = null) { var template = CreateRouteTemplate(templatePrefix); var constraintsDict = ObjectToDictionary(constraints); constraintsDict.Add(ClientRouteTokenName, new SpaRouteConstraint(ClientRouteTokenName)); routeBuilder.MapRoute(name, template, defaults, constraintsDict, dataTokens); } private static string CreateRouteTemplate(string templatePrefix) { templatePrefix = templatePrefix ?? string.Empty; if (templatePrefix.Contains("?")) { // TODO: Consider supporting this. The {*clientRoute} part should be added immediately before the '?' throw new ArgumentException("SPA fallback route templates don't support querystrings"); } if (templatePrefix.Contains("#")) { throw new ArgumentException( "SPA fallback route templates should not include # characters. The hash part of a URI does not get sent to the server."); } if (templatePrefix != string.Empty && !templatePrefix.EndsWith("/")) { templatePrefix += "/"; } return templatePrefix + $"{{*{ClientRouteTokenName}}}"; } private static IDictionary ObjectToDictionary(object value) => value as IDictionary ?? new RouteValueDictionary(value); } }