public static string CalculateResourceName( string httpMethod, string routeTemplate, IDictionary <string, object> routeValues, IDictionary <string, object>?defaults, out string?areaName, out string?controllerName, out string?actionName, bool addSlashPrefix, bool expandRouteTemplates) { // We could calculate the actual maximum size required by looping through all the // route values provided, comparing the parameter sizes to the parameter values // (only action/controller/area unless expandRouteParameters = true, // but doesn't seem worth it var sb = StringBuilderCache.Acquire(StringBuilderCache.MaxBuilderSize); sb.Append(httpMethod) .Append(' '); if (addSlashPrefix) { sb.Append('/'); } sb.Append(routeTemplate.ToLowerInvariant()); areaName = null; controllerName = null; actionName = null; foreach (var kvp in routeValues) { if (string.Equals(kvp.Key, "action", StringComparison.OrdinalIgnoreCase) && kvp.Value is string action) { actionName = action.ToLowerInvariant(); sb.Replace("{action}", actionName); } else if (string.Equals(kvp.Key, "controller", StringComparison.OrdinalIgnoreCase) && kvp.Value is string controller) { controllerName = controller.ToLowerInvariant(); sb.Replace("{controller}", controllerName); } else if (string.Equals(kvp.Key, "area", StringComparison.OrdinalIgnoreCase) && kvp.Value is string area) { areaName = area.ToLowerInvariant(); sb.Replace("{area}", areaName); } else if (expandRouteTemplates) { var valueAsString = kvp.Value as string ?? kvp.Value?.ToString() ?? string.Empty; if (UriHelpers.IsIdentifierSegment(valueAsString, 0, valueAsString.Length)) { // We're replacing the key with itself, so that we strip out all the additional parameters etc // We should probably be doing that for the non-expanded approach too, but we historically haven't ReplaceValue(sb, kvp.Key, kvp.Key, ReplaceType.ValueOnly); } else { ReplaceValue(sb, kvp.Key, valueAsString, ReplaceType.ValueAndBraces); } } } // Remove unused parameters from conventional route templates if (defaults is not null) { foreach (var kvp in defaults) { if (routeValues.ContainsKey(kvp.Key)) { continue; } ReplaceValue(sb, kvp.Key, null, replaceType: ReplaceType.ValueBracesAndLeadingSlash); } } return(StringBuilderCache.GetStringAndRelease(sb)); }