public string ResolveRouteTemplate(ActionDescriptor actionDescriptor, SwaggerRoutingOptions options) { var routes = ConventionalRoutingSwaggerGen.ROUTES; var template = string.Empty; var templateDefaults = new Dictionary <string, string>(); if (routes != null) { foreach (var router in routes) { templateDefaults.Clear(); var isDefaultArea = false; var isDefaultController = false; var isDefaultAction = false; var route = router as Route; if (route != null) { if (options?.IgnoreTemplateFunc != null) { var ignoreRoute = options.IgnoreTemplateFunc(route.RouteTemplate); if (ignoreRoute) { continue; } } var routeArea = GetRouteArea(route, out bool isAreaParameter); var routeController = GetRouteController(route, out var isControllerParameter); var routeAction = GetRouteAction(route, out bool isActionParameter); var actionDescArea = GetActionDescriptorArea(actionDescriptor); var actionDescController = GetActionDescriptorController(actionDescriptor); var actionDescAction = GetActionDescriptorAction(actionDescriptor); var routeMatchConfig = new MatchConfig(routeArea, routeController, routeAction) { IsAreaParameter = isAreaParameter, IsControllerParameter = isControllerParameter, IsActionParameter = isActionParameter }; var actionMatchConfig = new MatchConfig(actionDescArea, actionDescController, actionDescAction); if (MatchConfig.Match(routeMatchConfig, actionMatchConfig)) { var paramIndex = 0; var actionParametersUsed = new List <string>(); foreach (var segment in route.ParsedTemplate.Segments) { var firstPart = segment.Parts.First(); var hasConstraint = route.Constraints != null && route.Constraints.Any(c => c.Key.Equals(firstPart.Name)); IRouteConstraint routeConstraint = null; bool passConstraint = false; if (hasConstraint) { routeConstraint = route.Constraints[firstPart.Name]; } if (firstPart.IsLiteral) { template += $"{firstPart.Text}/"; } else if (firstPart.Name.Equals("area")) { if (string.IsNullOrEmpty(actionDescArea)) { template = null; break; } if (hasConstraint && (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint))) { passConstraint = PassConstraint(actionMatchConfig.Area, routeConstraint); if (!passConstraint) { template = null; break; } } if (!string.IsNullOrEmpty(actionMatchConfig.Area) && route.Defaults != null && route.Defaults.TryGetValue("area", out var defaultArea) && !string.IsNullOrEmpty(defaultArea?.ToString())) { isDefaultArea = actionMatchConfig.Area .Equals(defaultArea.ToString(), StringComparison.InvariantCultureIgnoreCase); if (isDefaultArea) { templateDefaults.Add("area", actionMatchConfig.Area); } } template += $"{actionMatchConfig.Area}/"; } else if (firstPart.Name.Equals("controller")) { if (hasConstraint && (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint))) { passConstraint = PassConstraint(actionMatchConfig.Controller, routeConstraint); if (!passConstraint) { template = null; break; } } if (route.Defaults != null && route.Defaults.TryGetValue("controller", out var defaultController) && !string.IsNullOrEmpty(defaultController?.ToString())) { var configController = WithNoSuffix(actionMatchConfig.Controller, "Controller") .ToLower(); isDefaultController = configController.Equals(defaultController.ToString(), StringComparison.InvariantCultureIgnoreCase); if (isDefaultController) { templateDefaults.Add("controller", $"{WithNoSuffix(actionMatchConfig.Controller, "Controller")}"); } } template += $"{WithNoSuffix(actionMatchConfig.Controller, "Controller")}/"; } else if (firstPart.Name.Equals("action")) { if (hasConstraint && (!IsCustomConstraint(routeConstraint) || IsRegexConstraint(routeConstraint))) { passConstraint = PassConstraint(actionMatchConfig.Action, routeConstraint); if (!passConstraint) { template = null; break; } } if (route.Defaults != null && route.Defaults.TryGetValue("action", out var defaultAction) && !string.IsNullOrEmpty(defaultAction?.ToString())) { isDefaultAction = actionMatchConfig.Action .Equals(defaultAction.ToString(), StringComparison.InvariantCultureIgnoreCase); if (isDefaultAction) { templateDefaults.Add("action", actionMatchConfig.Action); } } template += $"{actionMatchConfig.Action}/"; } else if (firstPart.IsParameter) { var hasActionDescParameter = HasActionDescriptorParameter(actionDescriptor, firstPart.Name, out var paramBindingInfo); if (hasActionDescParameter && hasConstraint) { var parameterInfo = actionDescriptor.Parameters.First(param => param.Name.Equals(firstPart.Name, StringComparison.InvariantCultureIgnoreCase)); passConstraint = PassConstraint(parameterInfo, routeConstraint); if (passConstraint && paramBindingInfo == null) { var routeParam = route.ParsedTemplate.Parameters[paramIndex]; if (routeParam.Name.Equals(firstPart.Name, StringComparison.CurrentCultureIgnoreCase) && !routeParam.IsOptional) { template += $"{{{firstPart.Name}}}/"; break; } } if (!passConstraint && parameterInfo.BindingInfo?.BindingSource.Id != "Query") { template = null; break; } if (parameterInfo?.BindingInfo?.BindingSource.Id == "Path") { templateDefaults.Clear(); } } if (hasActionDescParameter && paramBindingInfo != null) { actionParametersUsed.Add(firstPart.Name); template += $"{{{firstPart.Name}{(firstPart.IsOptional ? "?" : string.Empty)}}}/"; } else if (!firstPart.IsOptional) { if (hasConstraint && IsCustomConstraint(routeConstraint) || route.ParsedTemplate.Parameters.IndexOf(firstPart) != route.ParsedTemplate.Parameters.Count - 1) { template += $"{{{firstPart.Name}}}/"; } else { template = null; break; } } else if (actionDescriptor.Parameters.Count > 0) { var httpMethod = actionDescriptor.ActionConstraints .FirstOrDefault(c => c.GetType() == typeof(HttpMethodActionConstraint)); if (httpMethod != null && ((HttpMethodActionConstraint)httpMethod).HttpMethods.Count() == 1 && ((HttpMethodActionConstraint)httpMethod).HttpMethods.First().Equals("GET")) { var unusedParameters = actionDescriptor.Parameters.Where(param => !actionParametersUsed.Contains(param.Name)); if (!string.IsNullOrEmpty(template) && unusedParameters.All(param => param.BindingInfo == null)) { break; } template = null; break; } } paramIndex++; } } } // exit on first route match if (!string.IsNullOrEmpty(template)) { break; } } } } if (!string.IsNullOrEmpty(template)) { template = template.TrimEnd('/'); } if (!string.IsNullOrEmpty(template) && options.SkipDefaults && templateDefaults.Any()) { template = SkipDefaultsFromTemplate(templateDefaults, template); } return(template); }