private static HttpRouteValueDictionary FilterDataTokens(IHttpRoute route, string apiControllerAssembly) { var newDataTokens = new HttpRouteValueDictionary(); foreach (var dataToken in route.DataTokens) { var actionDescriptors = dataToken.Value as IEnumerable <HttpActionDescriptor>; if (actionDescriptors != null) { var newActionDescriptors = new List <HttpActionDescriptor>(); foreach (var actionDescriptor in actionDescriptors) { if (actionDescriptor.ControllerDescriptor.ControllerType.Assembly.FullName == apiControllerAssembly) { newActionDescriptors.Add(actionDescriptor); } } if (newActionDescriptors.Count > 0) { newDataTokens.Add(dataToken.Key, newActionDescriptors.ToArray()); } } else { newDataTokens.Add(dataToken.Key, dataToken.Value); } } return(newDataTokens); }
public OkActionResultWithPagingHeaders(string routename, HttpResponseMessage response, int page, int pagesize, int pagecount, int totalcount) { this.response = response; response.StatusCode = HttpStatusCode.OK; var requestparams = response.RequestMessage.GetQueryNameValuePairs(); var parameters = new HttpRouteValueDictionary(); foreach (KeyValuePair <string, string> kvp in requestparams) { parameters.Add(kvp.Key, kvp.Value); } var helper = new UrlHelper(response.RequestMessage); if (!parameters.Any(x => x.Key == "page")) { parameters.Add("page", page - 1); } else { parameters["page"] = page - 1; } var prevUrl = page > 0 ? helper.Link(routename, parameters) : ""; parameters["page"] = page + 1; var nextUrl = page < pagecount - 1 ? helper.Link(routename, parameters) : ""; response.Headers.Add("X-Paging-PageNo", page.ToString()); response.Headers.Add("X-Paging-PageSize", pagesize.ToString()); response.Headers.Add("X-Paging-PageCount", pagecount.ToString()); response.Headers.Add("X-Paging-TotalRecordCount", totalcount.ToString()); response.Headers.Add("X-Paging-PrevPage", prevUrl); response.Headers.Add("X-Paging-NextPage", nextUrl); }
public void GenerateSwaggerDocument_AddsParameterInfoUsingKnownAndUnknownHttpRouteConstraint() { // Arrange string apiEndpoint = "/api/{id}/{category}/HttpTriggerCSharp1"; string routeTemplate = apiEndpoint.Substring(1); HttpMethod[] allowedMethods = { HttpMethod.Get, HttpMethod.Post }; var httpMethodConstraint = new HttpMethodConstraint(allowedMethods); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add(ScriptConstants.HttpMethodConstraintName, httpMethodConstraint); constraints.Add("id", new IntRouteConstraint()); var httpRoute = new HttpRoute(routeTemplate, null, constraints); Dictionary <IHttpRoute, FunctionDescriptor> httpFunctions = new Dictionary <IHttpRoute, FunctionDescriptor>() { { httpRoute, new FunctionDescriptor("HttpTriggerCSharp1", null, new FunctionMetadata(), null, null, null, null) } }; var swaggerDocumentManager = new SwaggerDocumentManager(_scriptConfig); // Act var generatedDocument = swaggerDocumentManager.GenerateSwaggerDocument(httpFunctions); // Assert var swaggerdoc = generatedDocument.ToObject <SwaggerDocument>(); Assert.True(swaggerdoc.ApiEndpoints.ContainsKey(apiEndpoint)); Assert.Equal(swaggerdoc.ApiEndpoints.Keys.Count, 1); var httpOperations = swaggerdoc.ApiEndpoints[apiEndpoint]; Assert.Equal(httpOperations.Count, 2); foreach (var httpMethod in httpMethodConstraint.AllowedMethods) { Assert.True(httpOperations.ContainsKey(httpMethod.Method.ToString().ToLowerInvariant())); Assert.NotNull(httpOperations[httpMethod.Method.ToString().ToLowerInvariant()]); var inputParams = httpOperations[httpMethod.Method.ToString().ToLowerInvariant()].InputParameters.ToList(); Assert.Equal(inputParams.Count, 2); Assert.Equal(inputParams[0].DataType, SwaggerDataType.Integer.ToString().ToLowerInvariant()); Assert.Equal(inputParams[1].DataType, SwaggerDataType.String.ToString().ToLowerInvariant()); } }
public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }//, //constraints:new{sss=new HttpMethodConstraint(HttpMethod.Post)} ); HttpRouteValueDictionary defaults = new HttpRouteValueDictionary(); //defaults.Add("controller", "Demo"); //defaults.Add("action", "Get"); defaults.Add("val", 0); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add("val",new DoubleRouteConstraint()); HttpRoute route = new HttpRoute("customer/{controller}/{action}/{val}", defaults, constraints); config.Routes.Add("CustomerApi",route); }
/// <summary> /// Map odata route with query string or header constraints /// </summary> public static void MapODataServiceRoute( this HttpRouteCollection routes, string routeName, string routePrefix, IEdmModel model, IODataPathHandler pathHandler, IEnumerable <IODataRoutingConvention> routingConventions, object queryConstraints, object headerConstraints) { if (routes == null) { throw new ArgumentNullException(nameof(routes)); } string routeTemplate = string.IsNullOrEmpty(routePrefix) ? ODataRouteConstants.ODataPathTemplate : routePrefix + "/" + ODataRouteConstants.ODataPathTemplate; ODataVersionRouteConstraint routeConstraint = new ODataVersionRouteConstraint(pathHandler, model, routeName, routingConventions, queryConstraints, headerConstraints); var constraints = new HttpRouteValueDictionary(); constraints.Add(ODataRouteConstants.ConstraintName, routeConstraint); routes.MapHttpRoute( routeName, routeTemplate, defaults: null, constraints: constraints); }
/// <summary> /// Map odata route with query string or header constraints /// </summary> public static void MapODataRoute( this HttpRouteCollection routes, string routeName, string routePrefix, IEdmModel model, IODataPathHandler pathHandler, IEnumerable<IODataRoutingConvention> routingConventions, object queryConstraints, object headerConstraints) { if (routes == null) { throw new ArgumentNullException("routes"); } string routeTemplate = string.IsNullOrEmpty(routePrefix) ? ODataRouteConstants.ODataPathTemplate : (routePrefix + "/" + ODataRouteConstants.ODataPathTemplate); ODataVersionRouteConstraint routeConstraint = new ODataVersionRouteConstraint(pathHandler, model, routeName, routingConventions, queryConstraints, headerConstraints); var constraints = new HttpRouteValueDictionary(); constraints.Add(ODataRouteConstants.ConstraintName, routeConstraint); routes.MapHttpRoute( routeName, routeTemplate, defaults: null, constraints: constraints); }
public static void Register(HttpConfiguration config) { // Web API 配置和服务 // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional }//, //constraints:new{sss=new HttpMethodConstraint(HttpMethod.Post)} ); HttpRouteValueDictionary defaults = new HttpRouteValueDictionary(); //defaults.Add("controller", "Demo"); //defaults.Add("action", "Get"); defaults.Add("val", 0); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add("val", new DoubleRouteConstraint()); HttpRoute route = new HttpRoute("customer/{controller}/{action}/{val}", defaults, constraints); config.Routes.Add("CustomerApi", route); }
public Dictionary <string, string> GenerateVirtualPath() { Dictionary <string, string> result = new Dictionary <string, string>(); IHttpRoute defaultApi = RequestContext.Configuration.Routes["DefaultApi"]; IHttpRoute customerApi = RequestContext.Configuration.Routes["CustomerApi"]; HttpRouteValueDictionary vals = new HttpRouteValueDictionary(); vals.Add("controller", "Demo"); vals.Add("action", "Get"); vals.Add(HttpRoute.HttpRouteKey, true);//可能是必须 var defaultPath = defaultApi.GetVirtualPath(Request, vals); var customerPath = customerApi.GetVirtualPath(Request, vals); result.Add("DefaultApi", defaultPath.VirtualPath); result.Add("CustomerApi", customerPath.VirtualPath); return(result); }
public void Constructor_IsCaseInsensitive() { // Arrange HttpRouteValueDictionary routeValues = new HttpRouteValueDictionary(); // Act routeValues.Add("KEY", null); // Assert Assert.True(routeValues.ContainsKey("key")); }
public static string HttpRouteUrl(this HttpRequestMessage request, HttpMethod method, IDictionary <string, object> routeValues) { if (routeValues == null) { throw new ArgumentNullException("routeValues"); } if (!routeValues.ContainsKey("controller")) { throw new ArgumentException("'controller' key must be provided", "routeValues"); } routeValues = new HttpRouteValueDictionary(routeValues); if (!routeValues.ContainsKey(HttpRouteKey)) { routeValues.Add(HttpRouteKey, true); } string controllerName = routeValues["controller"].ToString(); routeValues.Remove("controller"); string actionName = string.Empty; if (routeValues.ContainsKey("action")) { actionName = routeValues["action"].ToString(); routeValues.Remove("action"); } IHttpRoute[] matchedRoutes = request.GetConfiguration().Services .GetApiExplorer().ApiDescriptions .Where(x => x.ActionDescriptor.ControllerDescriptor.ControllerName.Equals(controllerName, StringComparison.OrdinalIgnoreCase)) .Where(x => x.ActionDescriptor.SupportedHttpMethods.Contains(method)) .Where(x => string.IsNullOrEmpty(actionName) || x.ActionDescriptor.ActionName.Equals(actionName, StringComparison.OrdinalIgnoreCase)) .Select(x => new { route = x.Route, matches = x.ActionDescriptor.GetParameters() .Count(p => (!p.IsOptional) && (p.ParameterType.IsPrimitive || SimpleTypes.Contains(p.ParameterType)) && (routeValues.ContainsKey(p.ParameterName)) && (routeValues[p.ParameterName].GetType() == p.ParameterType)) }) .Where(x => x.matches > 0) .OrderBy(x => x.route.DataTokens["order"]) .ThenBy(x => x.route.DataTokens["precedence"]) .ThenByDescending(x => x.matches) .Select(x => x.route) .ToArray(); if (matchedRoutes.Length > 0) { IHttpVirtualPathData pathData = matchedRoutes[0].GetVirtualPath(request, routeValues); if (pathData != null) { return(pathData.VirtualPath); } } return(null); }
// // 摘要: // 通过查找路由的 System.Web.Http.Routing.HttpRouteData 来确定该路由是否是传入请求的匹配项。 // // 参数: // virtualPathRoot: // 虚拟路径根。 // // request: // HTTP 请求。 // // 返回结果: // 如果匹配,则为该路由的 System.Web.Http.Routing.HttpRouteData;否则为 null。 public override IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request) { var routes = GlobalConfiguration.Configuration.Routes; if (!request.RequestUri.PathAndQuery.StartsWith("/api/service/do", StringComparison.CurrentCultureIgnoreCase)) { return(null); } //Stream reqStream = request.Content.ReadAsStreamAsync().Result; //if (reqStream.CanSeek) // reqStream.Position = 0; //var content = new StreamReader(reqStream, Encoding.UTF8).ReadToEnd(); //string intfaceName; //var document = new XmlDocument(); //try //{ // document.LoadXml(content); // var intfaceNode = document.SelectSingleNode("//Interface"); // intfaceName = intfaceNode == null // ? null // : intfaceNode.InnerText.Trim(); //} //catch //{ // return null; //} if (!request.Headers.Contains("Interface")) { return(null); } var intfaceName = request.Headers.GetValues("Interface").First(); if (string.IsNullOrWhiteSpace(intfaceName) || !intfaceName.Contains(".") || intfaceName.StartsWith(".") || intfaceName.EndsWith(".")) { return(null); } var paths = intfaceName.Split('.'); if (paths.Length == 2) { var controller = paths[0]; var action = paths[1]; var defaults = new HttpRouteValueDictionary(new { id = RouteParameter.Optional }); var constraints = new HttpRouteValueDictionary(); constraints.Add("httpMethod", new HttpMethodConstraint(HttpMethod.Post)); var data = new HttpRouteData(new HttpRoute("api/service/do", defaults)); return(data); } return(null); }
// public Dictionary <string, IDictionary <string, object> > CheckRouteData() { var result = new Dictionary <string, IDictionary <string, object> >(); HttpRouteValueDictionary defaults = new HttpRouteValueDictionary(); defaults.Add("controller", "Demo"); defaults.Add("action", "Get"); defaults.Add("val", 0); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add("val", new DoubleRouteConstraint()); HttpRoute route = new HttpRoute("cutomer/{controller}/{action}/{val}", defaults, constraints); var customerRouteData = route.GetRouteData(RequestContext.VirtualPathRoot, Request); Request.GetRouteData(); result.Add("CustomerApi", customerRouteData == null ? null : customerRouteData.Values); var defaultRouteData = RequestContext.RouteData.Route.GetRouteData(RequestContext.VirtualPathRoot, Request); result.Add("DefaultApi", defaultRouteData == null ? null : defaultRouteData.Values); return(result); }
/// <summary> /// Builds the contents for the route table based on the attributes /// found on the method of a specific controller. /// </summary> /// <param name="routes"></param> /// <param name="controllerType"></param> /// <param name="method"></param> private static void BuildControllerMethodRoutes(HttpRouteCollection routes, Type controllerType, MethodInfo method) { // Grab the http route attributes from the current method. var attributes = (HttpRouteAttribute[])method.GetCustomAttributes(typeof(HttpRouteAttribute), true); if (attributes.Length != 0) { // Automatically grab the controller name and action name // from the method and controller type. string action = method.Name; string controller = controllerType.Name; // Translate the somewhat weird controller name into one the routing system // understands, by removing the Controller part from the name. if (controller.EndsWith("Controller", StringComparison.Ordinal)) { controller = controller.Substring(0, controller.IndexOf("Controller")); } // Generate a route for every HTTP route attribute found on the method foreach (var attribute in attributes) { var routeValueDictionary = new HttpRouteValueDictionary(); routeValueDictionary.Add("controller", controller); routeValueDictionary.Add("action", action); ResolveOptionalRouteParameters(attribute.UriTemplate, method, routeValueDictionary); // Create the route and attach the default route handler to it. var route = new HttpRoute(attribute.UriTemplate, routeValueDictionary, new HttpRouteValueDictionary(), new HttpRouteValueDictionary()); routes.Add(Guid.NewGuid().ToString(), route); } } }
// OPTIONAL: Add additional overloads for constraints, dataTokens, and handler // Converts | to / on incoming route public override IHttpRouteData GetRouteData(string virtualPathRoot, System.Net.Http.HttpRequestMessage request) { var routeData = base.GetRouteData(virtualPathRoot, request); // RouteData will be null if the URL or constraint didn't match if (routeData != null) { var newValues = new HttpRouteValueDictionary(); foreach (var r in routeData.Values) { newValues.Add(r.Key, r.Value.ToString().Replace("|", "/")); } routeData = new HttpRouteData(this, newValues); } return(routeData); }
public void CreateRoute_ValidatesConstraintType_StringRegex() { // Arrange var routes = new MockHttpRouteCollection(); var constraint = "product|products"; var constraints = new HttpRouteValueDictionary(); constraints.Add("custom", constraint); // Act var route = routes.CreateRoute("{controller}/{id}", null, constraints); // Assert Assert.NotNull(route.Constraints["custom"]); Assert.Equal(1, routes.TimesValidateConstraintCalled); }
private static void MatchCatchAll(PathContentSegment contentPathSegment, IEnumerable <string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { object obj2; string str = string.Join(string.Empty, remainingRequestSegments.ToArray <string>()); PathParameterSubsegment subsegment = contentPathSegment.Subsegments[0] as PathParameterSubsegment; if (str.Length > 0) { obj2 = str; } else { defaultValues.TryGetValue(subsegment.ParameterName, out obj2); } matchedValues.Add(subsegment.ParameterName, obj2); }
public void CreateRoute_ValidatesConstraintType_StringRegex() { // Arrange var routes = new MockHttpRouteCollection(); var constraint = "product|products"; var constraints = new HttpRouteValueDictionary(); constraints.Add("custom", constraint); // Act var route = routes.CreateRoute("{controller}/{id}", null, constraints); // Assert Assert.NotNull(route.Constraints["custom"]); Assert.Equal(1, routes.TimesValidateConstraintCalled); }
public void CreateRoute_ValidatesConstraintType_InvalidType() { // Arrange var routes = new HttpRouteCollection(); var constraint = new Uri("http://localhost/"); var constraints = new HttpRouteValueDictionary(); constraints.Add("custom", constraint); string expectedMessage = "The constraint entry 'custom' on the route with route template '{controller}/{id}' " + "must have a string value or be of a type which implements 'System.Web.Http.Routing.IHttpRouteConstraint'."; // Act & Assert Assert.Throws <InvalidOperationException>(() => routes.CreateRoute("{controller}/{id}", null, constraints), expectedMessage); }
public HttpRouteValueDictionary GetAllRouteValues(PortalAliasInfo portalAliasInfo, object routeValues) { var allRouteValues = new HttpRouteValueDictionary(routeValues); var segments = portalAliasInfo.HTTPAlias.Split('/'); if (segments.Length > 1) { for (int i = 1; i < segments.Length; i++) { var key = "prefix" + (i - 1).ToString(CultureInfo.InvariantCulture); var value = segments[i]; allRouteValues.Add(key, value); } } return(allRouteValues); }
/// <summary> /// Resolves any route parameters that have been marked as optional /// </summary> /// <param name="uriTemplate"></param> /// <param name="method"></param> /// <param name="routeValueDictionary"></param> private static void ResolveOptionalRouteParameters(string uriTemplate, MethodInfo method, HttpRouteValueDictionary routeValueDictionary) { Regex pattern = new Regex(@"{(\S+)}"); var methodParameters = method.GetParameters(); foreach (Match match in pattern.Matches(uriTemplate)) { string parameterName = match.Groups[1].Value; var parameter = methodParameters.FirstOrDefault(param => param.Name == parameterName); // Mark the route parameter as optional when there's a method parameter for it // and that method parameter is marked with [OptionalRouteParameter] if (parameter != null && parameter.GetCustomAttributes(typeof(OptionalRouteParameterAttribute), true).Length != 0) { routeValueDictionary.Add(parameterName, RouteParameter.Optional); } } }
public void CreateRoute_ValidatesConstraintType_IHttpRouteConstraint() { // Arrange var routes = new MockHostedHttpRouteCollection(new RouteCollection()); var constraint = new CustomHttpConstraint(); var constraints = new HttpRouteValueDictionary(); constraints.Add("custom", constraint); // Act var route = routes.CreateRoute("{controller}/{id}", null, constraints); // Assert Assert.NotNull(route.Constraints["custom"]); Assert.Equal(1, routes.TimesValidateConstraintCalled); }
public HttpRouteValueDictionary GetAllRouteValues(PortalAliasInfo portalAliasInfo, object routeValues) { var allRouteValues = new HttpRouteValueDictionary(routeValues); var segments = portalAliasInfo.HTTPAlias.Split('/'); if(segments.Length > 1) { for(int i = 1; i < segments.Length; i++) { var key = "prefix" + (i - 1).ToString(CultureInfo.InvariantCulture); var value = segments[i]; allRouteValues.Add(key, value); } } return allRouteValues; }
public void GenerateSwaggerDocument_CreatesSwaggerDocument_WithSelectHttpMethods() { // Arrange string apiEndpoint = "/api/HttpTriggerCSharp1"; string routeTemplate = apiEndpoint.Substring(1); HttpMethod[] allowedMethods = { HttpMethod.Get, HttpMethod.Post }; var httpMethodConstraint = new HttpMethodConstraint(allowedMethods); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add(ScriptConstants.HttpMethodConstraintName, httpMethodConstraint); var httpRoute = new HttpRoute(routeTemplate, null, constraints); Dictionary <IHttpRoute, FunctionDescriptor> httpFunctions = new Dictionary <IHttpRoute, FunctionDescriptor>() { { httpRoute, new FunctionDescriptor("HttpTriggerCSharp1", null, new FunctionMetadata(), null, null, null, null) } }; var swaggerDocumentManager = new SwaggerDocumentManager(_scriptConfig); // Act var generatedDocument = swaggerDocumentManager.GenerateSwaggerDocument(httpFunctions); // Assert var swaggerdoc = generatedDocument.ToObject <SwaggerDocument>(); Assert.True(swaggerdoc.ApiEndpoints.ContainsKey(apiEndpoint)); Assert.Equal(swaggerdoc.ApiEndpoints.Keys.Count, 1); var httpOperations = swaggerdoc.ApiEndpoints[apiEndpoint]; Assert.Equal(httpOperations.Count, 2); foreach (var httpMethod in httpMethodConstraint.AllowedMethods) { Assert.True(httpOperations.ContainsKey(httpMethod.Method.ToString().ToLowerInvariant())); Assert.NotNull(httpOperations[httpMethod.Method.ToString().ToLowerInvariant()]); } }
private static void MatchCatchAll(SwaggerPathContentSegment contentPathSegment, IEnumerable <string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { string remainingRequest = String.Join(String.Empty, remainingRequestSegments.ToArray()); SwaggerPathParameterSubSegment catchAllSegment = contentPathSegment.Subsegments[0] as SwaggerPathParameterSubSegment; object catchAllValue; if (remainingRequest.Length > 0) { catchAllValue = remainingRequest; } else { defaultValues.TryGetValue(catchAllSegment.ParameterName, out catchAllValue); } matchedValues.Add(catchAllSegment.ParameterName, catchAllValue); }
// // 摘要: // 映射指定的路由模板并设置默认的路由值、约束和终结点消息处理程序。 // // 参数: // routes: // 应用程序的路由的集合。 // // name: // 要映射的路由的名称。 // // routeTemplate: // 路由的路由模板。 // // defaults: // 一个包含默认路由值的对象。 // // constraints: // 一组表达式,用于指定 routeTemplate 的值。 // // handler: // 请求将被调度到的处理程序。 // // 返回结果: // 对映射路由的引用。 public static RouteBase MapHttpRouteWithNamespance(this RouteCollection routes, string name, string routeTemplate, object defaults, string namespaces, object constraints, HttpMessageHandler handler) { if (routes == null) { throw new ArgumentNullException("routes"); } HttpRouteValueDictionary defaultsDictionary = new HttpRouteValueDictionary(defaults); HttpRouteValueDictionary constraintsDictionary = new HttpRouteValueDictionary(constraints); defaultsDictionary.Add("namespace", namespaces); var httpRoute = (RouteBase)GlobalConfiguration.Configuration.Routes.CreateRoute(routeTemplate, defaultsDictionary, constraintsDictionary, dataTokens: null, handler: handler); //Route route = httpRoute.OriginalRoute; routes.Add(name, httpRoute); return(httpRoute); }
public static void SetRequest(this ApiController controller, string controllerPrefix, HttpMethod method, string requestUri) { controller.Configuration = new HttpConfiguration(); var route = controller.Configuration.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); var routeValues = new HttpRouteValueDictionary(); routeValues.Add("controller", controllerPrefix); var routeData = new HttpRouteData(route, routeValues); controller.Request = new HttpRequestMessage(method, requestUri); controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, controller.Configuration); controller.Request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData); }
/// <summary> /// Establishes the Request and ControllerContext properties of the specified ApiController with test-specific values /// </summary> /// <param name="controller">The WebApiController to set up</param> /// <param name="method">Defaults to "GET"</param> /// <param name="controllerName">Defaults to "ctrl"</param> /// <param name="actionName">Defaults to "method"</param> /// <param name="id">Defaults to missing</param> public static void SetupForUnitTest(this ApiController controller, HttpMethod method = null, string controllerName = "ctrl", string actionName = "method", string id = null) { var config = new HttpConfiguration(); WebApiConfig.Register(config); var url = string.Format("http://localhost/api/v1/{0}/{1}{2}", controllerName, actionName, id == null ? "" : "/" + id); var request = new HttpRequestMessage(method ?? HttpMethod.Get, url); var httpRouteValueDictionary = new HttpRouteValueDictionary { { "controller", controllerName }, { "action", "method" } }; if (id != null) { httpRouteValueDictionary.Add("id", id); } var routeData = new HttpRouteData(config.Routes[0], httpRouteValueDictionary); controller.ControllerContext = new HttpControllerContext(config, routeData, request); controller.Request = request; controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config; }
/// <summary> /// Builds the contents for the route table based on the attributes /// found on a specific controller type. /// </summary> /// <param name="routes"></param> /// <param name="controllerType"></param> private static void BuildControllerRoutes(HttpRouteCollection routes, Type controllerType) { var attributes = (HttpRouteAttribute[])controllerType.GetCustomAttributes(typeof(HttpRouteAttribute), true); string controller = controllerType.Name; // Translate the somewhat weird controller name into one the routing system // understands, by removing the Controller part from the name. if (controller.EndsWith("Controller", StringComparison.Ordinal)) { controller = controller.Substring(0, controller.IndexOf("Controller")); } foreach (var attribute in attributes) { var routeValuesDictionary = new HttpRouteValueDictionary(); routeValuesDictionary.Add("controller", controller); // Create the route and attach the default route handler to it. var route = new HttpRoute(attribute.UriTemplate, routeValuesDictionary, new HttpRouteValueDictionary(), new HttpRouteValueDictionary()); routes.Add(Guid.NewGuid().ToString(), route); } }
public HelpController() { helpRouteValues = new HttpRouteValueDictionary(new { controller = "Help" }); helpRouteValues.Add(HttpRoute.HttpRouteKey, true); }
public void CreateRoute_ValidatesConstraintType_InvalidType() { // Arrange var routes = new HttpRouteCollection(); var constraint = new Uri("http://localhost/"); var constraints = new HttpRouteValueDictionary(); constraints.Add("custom", constraint); string expectedMessage = "The constraint entry 'custom' on the route with route template '{controller}/{id}' " + "must have a string value or be of a type which implements 'System.Web.Http.Routing.IHttpRouteConstraint'."; // Act & Assert Assert.Throws<InvalidOperationException>(() => routes.CreateRoute("{controller}/{id}", null, constraints), expectedMessage); }
private static string GetHttpRouteHelper(HttpRequestMessage request, string routeName, IDictionary<string, object> routeValues) { if (routeValues == null) { // If no route values were passed in at all we have to create a new dictionary // so that we can add the extra "httproute" key. routeValues = new HttpRouteValueDictionary(); routeValues.Add(HttpRoute.HttpRouteKey, true); } else { // Copy the dictionary so that we can guarantee that routeValues uses an OrdinalIgnoreCase comparer // and to add the extra "httproute" key used by all Web API routes to disambiguate them from other MVC routes. routeValues = new HttpRouteValueDictionary(routeValues); if (!routeValues.ContainsKey(HttpRoute.HttpRouteKey)) { routeValues.Add(HttpRoute.HttpRouteKey, true); } } HttpConfiguration configuration = request.GetConfiguration(); if (configuration == null) { throw Error.InvalidOperation(SRResources.HttpRequestMessageExtensions_NoConfiguration); } IHttpVirtualPathData vpd = configuration.Routes.GetVirtualPath( request: request, name: routeName, values: routeValues); if (vpd == null) { return null; } return vpd.VirtualPath; }
/// <summary> /// Builds the contents for the route table based on the attributes /// found on the method of a specific controller. /// </summary> /// <param name="routes"></param> /// <param name="controllerType"></param> /// <param name="method"></param> private static void BuildControllerMethodRoutes(HttpRouteCollection routes, Type controllerType, MethodInfo method) { // Grab the http route attributes from the current method. var attributes = (HttpRouteAttribute[]) method.GetCustomAttributes(typeof(HttpRouteAttribute), true); if (attributes.Length != 0) { // Automatically grab the controller name and action name // from the method and controller type. string action = method.Name; string controller = controllerType.Name; // Translate the somewhat weird controller name into one the routing system // understands, by removing the Controller part from the name. if (controller.EndsWith("Controller", StringComparison.Ordinal)) { controller = controller.Substring(0, controller.IndexOf("Controller")); } // Generate a route for every HTTP route attribute found on the method foreach (var attribute in attributes) { var routeValueDictionary = new HttpRouteValueDictionary(); routeValueDictionary.Add("controller", controller); routeValueDictionary.Add("action", action); ResolveOptionalRouteParameters(attribute.UriTemplate, method, routeValueDictionary); // Create the route and attach the default route handler to it. var route = new HttpRoute(attribute.UriTemplate, routeValueDictionary, new HttpRouteValueDictionary(), new HttpRouteValueDictionary()); routes.Add(Guid.NewGuid().ToString(), route); } } }
private static HttpRouteValueDictionary Match(RoutingContext context, HttpRouteValueDictionary defaultValues) { List <string> pathSegments = context.PathSegments; if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } HttpRouteValueDictionary matchedValues = new HttpRouteValueDictionary(); bool flag = false; bool flag2 = false; for (int i = 0; i < thisPathSegments.Count; i++) { PathSegment segment = thisPathSegments[i]; if (pathSegments.Count <= i) { flag = true; } string a = flag ? null : pathSegments[i]; if (segment is PathSeparatorSegment) { if (!flag && !string.Equals(a, "/", StringComparison.Ordinal)) { return(null); } } else { PathContentSegment contentPathSegment = segment as PathContentSegment; if (contentPathSegment != null) { if (contentPathSegment.IsCatchAll) { MatchCatchAll(contentPathSegment, pathSegments.Skip <string>(i), defaultValues, matchedValues); flag2 = true; } else if (!MatchContentPathSegment(contentPathSegment, a, defaultValues, matchedValues)) { return(null); } } } } if (!flag2 && (this.PathSegments.Count < pathSegments.Count)) { for (int j = thisPathSegments.Count; j < pathSegments.Count; j++) { if (!IsSeparator(pathSegments[j])) { return(null); } } } if (defaultValues != null) { foreach (KeyValuePair <string, object> pair in defaultValues) { if (!matchedValues.ContainsKey(pair.Key)) { matchedValues.Add(pair.Key, pair.Value); } } } return(matchedValues); }
public HttpRouteValueDictionary Match(string virtualPath, HttpRouteValueDictionary defaultValues) { IList<string> requestPathSegments = HttpRouteParser.SplitUriToPathSegmentStrings(virtualPath); if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } HttpRouteValueDictionary matchedValues = new HttpRouteValueDictionary(); // This flag gets set once all the data in the URI has been parsed through, but // the route we're trying to match against still has more parts. At this point // we'll only continue matching separator characters and parameters that have // default values. bool ranOutOfStuffToParse = false; // This value gets set once we start processing a catchall parameter (if there is one // at all). Once we set this value we consume all remaining parts of the URI into its // parameter value. bool usedCatchAllParameter = false; for (int i = 0; i < _pathSegments.Count; i++) { PathSegment pathSegment = _pathSegments[i]; if (requestPathSegments.Count <= i) { ranOutOfStuffToParse = true; } string requestPathSegment = ranOutOfStuffToParse ? null : requestPathSegments[i]; if (pathSegment is PathSeparatorSegment) { if (ranOutOfStuffToParse) { // If we're trying to match a separator in the route but there's no more content, that's OK } else { if (!String.Equals(requestPathSegment, "/", StringComparison.Ordinal)) { return null; } } } else { PathContentSegment contentPathSegment = pathSegment as PathContentSegment; if (contentPathSegment != null) { if (contentPathSegment.IsCatchAll) { Contract.Assert(i == (_pathSegments.Count - 1), "If we're processing a catch-all, we should be on the last route segment."); MatchCatchAll(contentPathSegment, requestPathSegments.Skip(i), defaultValues, matchedValues); usedCatchAllParameter = true; } else { if (!MatchContentPathSegment(contentPathSegment, requestPathSegment, defaultValues, matchedValues)) { return null; } } } else { Contract.Assert(false, "Invalid path segment type"); } } } if (!usedCatchAllParameter) { if (_pathSegments.Count < requestPathSegments.Count) { // If we've already gone through all the parts defined in the route but the URI // still contains more content, check that the remaining content is all separators. for (int i = _pathSegments.Count; i < requestPathSegments.Count; i++) { if (!HttpRouteParser.IsSeparator(requestPathSegments[i])) { return null; } } } } // Copy all remaining default values to the route data if (defaultValues != null) { foreach (var defaultValue in defaultValues) { if (!matchedValues.ContainsKey(defaultValue.Key)) { matchedValues.Add(defaultValue.Key, defaultValue.Value); } } } return matchedValues; }
private static bool MatchContentPathSegment(PathContentSegment routeSegment, string requestPathSegment, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { if (String.IsNullOrEmpty(requestPathSegment)) { // If there's no data to parse, we must have exactly one parameter segment and no other segments - otherwise no match if (routeSegment.Subsegments.Count > 1) { return false; } PathParameterSubsegment parameterSubsegment = routeSegment.Subsegments[0] as PathParameterSubsegment; if (parameterSubsegment == null) { return false; } // We must have a default value since there's no value in the request URI object parameterValue; if (defaultValues.TryGetValue(parameterSubsegment.ParameterName, out parameterValue)) { // If there's a default value for this parameter, use that default value matchedValues.Add(parameterSubsegment.ParameterName, parameterValue); return true; } else { // If there's no default value, this segment doesn't match return false; } } // Find last literal segment and get its last index in the string int lastIndex = requestPathSegment.Length; int indexOfLastSegmentUsed = routeSegment.Subsegments.Count - 1; PathParameterSubsegment parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value PathLiteralSubsegment lastLiteral = null; // Keeps track of the left-most literal we've encountered while (indexOfLastSegmentUsed >= 0) { int newLastIndex = lastIndex; PathParameterSubsegment parameterSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as PathParameterSubsegment; if (parameterSubsegment != null) { // Hold on to the parameter so that we can fill it in when we locate the next literal parameterNeedsValue = parameterSubsegment; } else { PathLiteralSubsegment literalSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as PathLiteralSubsegment; if (literalSubsegment != null) { lastLiteral = literalSubsegment; int startIndex = lastIndex - 1; // If we have a pending parameter subsegment, we must leave at least one character for that if (parameterNeedsValue != null) { startIndex--; } if (startIndex < 0) { return false; } int indexOfLiteral = requestPathSegment.LastIndexOf(literalSubsegment.Literal, startIndex, StringComparison.OrdinalIgnoreCase); if (indexOfLiteral == -1) { // If we couldn't find this literal index, this segment cannot match return false; } // If the first subsegment is a literal, it must match at the right-most extent of the request URI. // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/". // This check is related to the check we do at the very end of this function. if (indexOfLastSegmentUsed == (routeSegment.Subsegments.Count - 1)) { if ((indexOfLiteral + literalSubsegment.Literal.Length) != requestPathSegment.Length) { return false; } } newLastIndex = indexOfLiteral; } else { Contract.Assert(false, "Invalid path segment type"); } } if ((parameterNeedsValue != null) && (((lastLiteral != null) && (parameterSubsegment == null)) || (indexOfLastSegmentUsed == 0))) { // If we have a pending parameter that needs a value, grab that value int parameterStartIndex; int parameterTextLength; if (lastLiteral == null) { if (indexOfLastSegmentUsed == 0) { parameterStartIndex = 0; } else { parameterStartIndex = newLastIndex; Contract.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above"); } parameterTextLength = lastIndex; } else { // If we're getting a value for a parameter that is somewhere in the middle of the segment if ((indexOfLastSegmentUsed == 0) && (parameterSubsegment != null)) { parameterStartIndex = 0; parameterTextLength = lastIndex; } else { parameterStartIndex = newLastIndex + lastLiteral.Literal.Length; parameterTextLength = lastIndex - parameterStartIndex; } } string parameterValueString = requestPathSegment.Substring(parameterStartIndex, parameterTextLength); if (String.IsNullOrEmpty(parameterValueString)) { // If we're here that means we have a segment that contains multiple sub-segments. // For these segments all parameters must have non-empty values. If the parameter // has an empty value it's not a match. return false; } else { // If there's a value in the segment for this parameter, use the subsegment value matchedValues.Add(parameterNeedsValue.ParameterName, parameterValueString); } parameterNeedsValue = null; lastLiteral = null; } lastIndex = newLastIndex; indexOfLastSegmentUsed--; } // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire* // request URI in order for it to be a match. // This check is related to the check we do earlier in this function for LiteralSubsegments. return (lastIndex == 0) || (routeSegment.Subsegments[0] is PathParameterSubsegment); }
private string ParseRouteTemplate(string routeTemplate, HttpRouteValueDictionary defaults, HttpRouteValueDictionary constraints) { Contract.Assert(defaults != null); Contract.Assert(constraints != null); MatchCollection parameterMatches = _parameterRegex.Matches(routeTemplate); foreach (Match parameterMatch in parameterMatches) { string parameterName = parameterMatch.Groups["parameterName"].Value; // We may need to strip out the initial wildcard used for wildcard parameters if (parameterName.StartsWith("*", StringComparison.OrdinalIgnoreCase)) { parameterName = parameterName.Substring(1); } // Add the default value if present Group defaultValueGroup = parameterMatch.Groups["defaultValue"]; object defaultValue = GetDefaultValue(defaultValueGroup); if (defaultValue != null) { defaults.Add(parameterName, defaultValue); } // Register inline constraints if present Group constraintGroup = parameterMatch.Groups["constraint"]; bool isOptional = defaultValue == RouteParameter.Optional; IHttpRouteConstraint constraint = GetInlineConstraint(constraintGroup, isOptional); if (constraint != null) { constraints.Add(parameterName, constraint); } } // Replaces parameter matches with just the parameter name in braces // Strips out the optional '?', default value, inline constraints return _parameterRegex.Replace(routeTemplate, @"{${parameterName}}"); }
/// <summary> /// Builds an <see cref="IHttpRoute"/> for a particular action. /// </summary> /// <param name="routeTemplate">The tokenized route template for the route.</param> /// <param name="httpMethods">The HTTP methods supported by the route.</param> /// <param name="controllerName">The name of the associated controller.</param> /// <param name="actionName">The name of the associated action.</param> /// <returns>The generated <see cref="IHttpRoute"/>.</returns> public virtual IHttpRoute BuildHttpRoute(string routeTemplate, IEnumerable<HttpMethod> httpMethods, string controllerName, string actionName) { if (routeTemplate == null) { throw Error.ArgumentNull("routeTemplate"); } if (controllerName == null) { throw Error.ArgumentNull("controllerName"); } if (actionName == null) { throw Error.ArgumentNull("actionName"); } HttpRouteValueDictionary defaults = new HttpRouteValueDictionary { { "controller", controllerName }, { "action", actionName } }; HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); if (httpMethods != null) { // Current method constraint implementation is inefficient since it matches before running the constraint. // Consider checking the HTTP method first in a custom route as a performance optimization. constraints.Add("httpMethod", new HttpMethodConstraint(httpMethods.ToArray())); } string detokenizedRouteTemplate = ParseRouteTemplate(routeTemplate, defaults, constraints); return BuildHttpRoute(defaults, constraints, detokenizedRouteTemplate); }
private static bool MatchContentPathSegment(SwaggerPathContentSegment routeSegment, string requestPathSegment, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { if (String.IsNullOrEmpty(requestPathSegment)) { // If there's no data to parse, we must have exactly one parameter segment and no other segments - otherwise no match if (routeSegment.Subsegments.Count > 1) { return(false); } SwaggerPathParameterSubSegment parameterSubsegment = routeSegment.Subsegments[0] as SwaggerPathParameterSubSegment; if (parameterSubsegment == null) { return(false); } // We must have a default value since there's no value in the request URI object parameterValue; if (defaultValues.TryGetValue(parameterSubsegment.ParameterName, out parameterValue)) { // If there's a default value for this parameter, use that default value matchedValues.Add(parameterSubsegment.ParameterName, parameterValue); return(true); } else { // If there's no default value, this segment doesn't match return(false); } } // Find last literal segment and get its last index in the string int lastIndex = requestPathSegment.Length; int indexOfLastSegmentUsed = routeSegment.Subsegments.Count - 1; SwaggerPathParameterSubSegment parameterNeedsValue = null; // Keeps track of a parameter segment that is pending a value SwaggerPathLiteralSubsegment lastLiteral = null; // Keeps track of the left-most literal we've encountered while (indexOfLastSegmentUsed >= 0) { int newLastIndex = lastIndex; SwaggerPathParameterSubSegment parameterSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as SwaggerPathParameterSubSegment; if (parameterSubsegment != null) { // Hold on to the parameter so that we can fill it in when we locate the next literal parameterNeedsValue = parameterSubsegment; } else { SwaggerPathLiteralSubsegment literalSubsegment = routeSegment.Subsegments[indexOfLastSegmentUsed] as SwaggerPathLiteralSubsegment; if (literalSubsegment != null) { lastLiteral = literalSubsegment; int startIndex = lastIndex - 1; // If we have a pending parameter subsegment, we must leave at least one character for that if (parameterNeedsValue != null) { startIndex--; } if (startIndex < 0) { return(false); } int indexOfLiteral = requestPathSegment.LastIndexOf(literalSubsegment.Literal, startIndex, StringComparison.OrdinalIgnoreCase); if (indexOfLiteral == -1) { // If we couldn't find this literal index, this segment cannot match return(false); } // If the first subsegment is a literal, it must match at the right-most extent of the request URI. // Without this check if your route had "/Foo/" we'd match the request URI "/somethingFoo/". // This check is related to the check we do at the very end of this function. if (indexOfLastSegmentUsed == (routeSegment.Subsegments.Count - 1)) { if ((indexOfLiteral + literalSubsegment.Literal.Length) != requestPathSegment.Length) { return(false); } } newLastIndex = indexOfLiteral; } else { Contract.Assert(false, "Invalid path segment type"); } } if ((parameterNeedsValue != null) && (((lastLiteral != null) && (parameterSubsegment == null)) || (indexOfLastSegmentUsed == 0))) { // If we have a pending parameter that needs a value, grab that value int parameterStartIndex; int parameterTextLength; if (lastLiteral == null) { if (indexOfLastSegmentUsed == 0) { parameterStartIndex = 0; } else { parameterStartIndex = newLastIndex; Contract.Assert(false, "indexOfLastSegementUsed should always be 0 from the check above"); } parameterTextLength = lastIndex; } else { // If we're getting a value for a parameter that is somewhere in the middle of the segment if ((indexOfLastSegmentUsed == 0) && (parameterSubsegment != null)) { parameterStartIndex = 0; parameterTextLength = lastIndex; } else { parameterStartIndex = newLastIndex + lastLiteral.Literal.Length; parameterTextLength = lastIndex - parameterStartIndex; } } string parameterValueString = requestPathSegment.Substring(parameterStartIndex, parameterTextLength); if (String.IsNullOrEmpty(parameterValueString)) { // If we're here that means we have a segment that contains multiple sub-segments. // For these segments all parameters must have non-empty values. If the parameter // has an empty value it's not a match. return(false); } else { // If there's a value in the segment for this parameter, use the subsegment value matchedValues.Add(parameterNeedsValue.ParameterName, parameterValueString); } parameterNeedsValue = null; lastLiteral = null; } lastIndex = newLastIndex; indexOfLastSegmentUsed--; } // If the last subsegment is a parameter, it's OK that we didn't parse all the way to the left extent of // the string since the parameter will have consumed all the remaining text anyway. If the last subsegment // is a literal then we *must* have consumed the entire text in that literal. Otherwise we end up matching // the route "Foo" to the request URI "somethingFoo". Thus we have to check that we parsed the *entire* // request URI in order for it to be a match. // This check is related to the check we do earlier in this function for LiteralSubsegments. return((lastIndex == 0) || (routeSegment.Subsegments[0] is SwaggerPathParameterSubSegment)); }
public HttpRouteValueDictionary Match(string virtualPath, HttpRouteValueDictionary defaultValues) { IList <string> requestPathSegments = SwaggerHttpRouteParser.SplitUriToPathSegmentStrings(virtualPath); if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } HttpRouteValueDictionary matchedValues = new HttpRouteValueDictionary(); // This flag gets set once all the data in the URI has been parsed through, but // the route we're trying to match against still has more parts. At this point // we'll only continue matching separator characters and parameters that have // default values. bool ranOutOfStuffToParse = false; // This value gets set once we start processing a catchall parameter (if there is one // at all). Once we set this value we consume all remaining parts of the URI into its // parameter value. bool usedCatchAllParameter = false; for (int i = 0; i < _pathSegments.Count; i++) { SwaggerPathSegment pathSegment = _pathSegments[i]; if (requestPathSegments.Count <= i) { ranOutOfStuffToParse = true; } string requestPathSegment = ranOutOfStuffToParse ? null : requestPathSegments[i]; if (pathSegment is SwaggerPathSeparatorSegment) { if (ranOutOfStuffToParse) { // If we're trying to match a separator in the route but there's no more content, that's OK } else { if (!String.Equals(requestPathSegment, "/", StringComparison.Ordinal)) { return(null); } } } else { SwaggerPathContentSegment contentPathSegment = pathSegment as SwaggerPathContentSegment; if (contentPathSegment != null) { if (contentPathSegment.IsCatchAll) { Contract.Assert(i == (_pathSegments.Count - 1), "If we're processing a catch-all, we should be on the last route segment."); MatchCatchAll(contentPathSegment, requestPathSegments.Skip(i), defaultValues, matchedValues); usedCatchAllParameter = true; } else { if (!MatchContentPathSegment(contentPathSegment, requestPathSegment, defaultValues, matchedValues)) { return(null); } } } else { Contract.Assert(false, "Invalid path segment type"); } } } if (!usedCatchAllParameter) { if (_pathSegments.Count < requestPathSegments.Count) { // If we've already gone through all the parts defined in the route but the URI // still contains more content, check that the remaining content is all separators. for (int i = _pathSegments.Count; i < requestPathSegments.Count; i++) { if (!SwaggerHttpRouteParser.IsSeparator(requestPathSegments[i])) { return(null); } } } } // Copy all remaining default values to the route data if (defaultValues != null) { foreach (var defaultValue in defaultValues) { if (!matchedValues.ContainsKey(defaultValue.Key)) { matchedValues.Add(defaultValue.Key, defaultValue.Value); } } } return(matchedValues); }
public void GenerateSwaggerDocument_CreatesBasicSwaggerDocument() { string apiEndpoint = "/api/HttpTriggerCSharp1"; string routeTemplate = apiEndpoint.Substring(1); HttpMethod[] allowedMethods = { HttpMethod.Get }; var httpMethodConstraint = new HttpMethodConstraint(allowedMethods); HttpRouteValueDictionary constraints = new HttpRouteValueDictionary(); constraints.Add(ScriptConstants.HttpMethodConstraintName, httpMethodConstraint); var httpRoute = new HttpRoute(routeTemplate, null, constraints); Dictionary <IHttpRoute, FunctionDescriptor> httpFunctions = new Dictionary <IHttpRoute, FunctionDescriptor>() { { httpRoute, new FunctionDescriptor("HttpTriggerCSharp1", null, new FunctionMetadata(), null, null, null, null) } }; // Act var swaggerDocumentManager = new SwaggerDocumentManager(_scriptConfig); var generatedDocument = swaggerDocumentManager.GenerateSwaggerDocument(httpFunctions); string hostName = Environment.GetEnvironmentVariable(EnvironmentSettingNames.AzureWebsiteHostName); if (hostName == null) { generatedDocument["info"]["title"] = string.Empty; generatedDocument["host"] = string.Empty; } string expectedSwagger = @" { 'swagger': '2.0', 'info': { 'title': 'localhost', 'version': '1.0.0' }, 'host': 'localhost', 'basePath': '/', 'schemes': [ 'https', 'http' ], 'paths': { '/api/HttpTriggerCSharp1': { 'get': { 'operationId': '/api/HttpTriggerCSharp1/get', 'produces': [], 'consumes': [], 'parameters': [], 'description': 'Replace with Operation Object #http://swagger.io/specification/#operationObject', 'responses': { '200': { 'description': 'Success operation' } }, 'security': [ { 'apikeyQuery': [] } ] } } }, 'definitions': {}, 'securityDefinitions': { 'apikeyQuery': { 'type': 'apiKey', 'name': 'code', 'in': 'query' } } }".Replace("localhost", hostName); expectedSwagger = JObject.Parse(expectedSwagger).ToString(Formatting.None); Assert.Equal(expectedSwagger, generatedDocument.ToString(Formatting.None)); }
private static IDictionary<string, object> GetRouteDictionaryWithoutHttpRouteKey(IDictionary<string, object> routeValues) { var newRouteValues = new HttpRouteValueDictionary(); if (routeValues != null) { foreach (var routeValue in routeValues) { if (!String.Equals(routeValue.Key, HttpRouteKey, StringComparison.OrdinalIgnoreCase)) { newRouteValues.Add(routeValue.Key, routeValue.Value); } } } return newRouteValues; }
/// <summary> /// Resolves any route parameters that have been marked as optional /// </summary> /// <param name="uriTemplate"></param> /// <param name="method"></param> /// <param name="routeValueDictionary"></param> private static void ResolveOptionalRouteParameters(string uriTemplate, MethodInfo method, HttpRouteValueDictionary routeValueDictionary) { Regex pattern = new Regex(@"{(\S+)}"); var methodParameters = method.GetParameters(); foreach (Match match in pattern.Matches(uriTemplate)) { string parameterName = match.Groups[1].Value; var parameter = methodParameters.FirstOrDefault(param => param.Name == parameterName); // Mark the route parameter as optional when there's a method parameter for it // and that method parameter is marked with [OptionalRouteParameter] if (parameter != null && parameter.GetCustomAttributes(typeof(OptionalRouteParameterAttribute), true).Length != 0) { routeValueDictionary.Add(parameterName, RouteParameter.Optional); } } }
public HelpController() { helpRouteValues = new HttpRouteValueDictionary(new { controller = "Help" }); helpRouteValues.Add(HttpRoute.HttpRouteKey, true); }
private static HttpRouteValueDictionary BuildHttpRouteValueDictionary(IEnumerable<RouteConfigurationParameter> defaultParameters) { var result = new HttpRouteValueDictionary(); foreach (var defaultParameter in defaultParameters) { var value = defaultParameter.IsOptional ? RouteParameter.Optional : defaultParameter.Value; result.Add(defaultParameter.Name, value); } return result; }
public BoundRouteTemplate Bind(IDictionary<string, object> currentValues, IDictionary<string, object> values, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary constraints) { if (currentValues == null) { currentValues = new HttpRouteValueDictionary(); } if (values == null) { values = new HttpRouteValueDictionary(); } if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } // The set of values we should be using when generating the URI in this route HttpRouteValueDictionary acceptedValues = new HttpRouteValueDictionary(); // Keep track of which new values have been used HashSet<string> unusedNewValues = new HashSet<string>(values.Keys, StringComparer.OrdinalIgnoreCase); // Step 1: Get the list of values we're going to try to use to match and generate this URI // Find out which entries in the URI are valid for the URI we want to generate. // If the URI had ordered parameters a="1", b="2", c="3" and the new values // specified that b="9", then we need to invalidate everything after it. The new // values should then be a="1", b="9", c=<no value>. ForEachParameter(_pathSegments, delegate(PathParameterSubsegment parameterSubsegment) { // If it's a parameter subsegment, examine the current value to see if it matches the new value string parameterName = parameterSubsegment.ParameterName; object newParameterValue; bool hasNewParameterValue = values.TryGetValue(parameterName, out newParameterValue); if (hasNewParameterValue) { unusedNewValues.Remove(parameterName); } object currentParameterValue; bool hasCurrentParameterValue = currentValues.TryGetValue(parameterName, out currentParameterValue); if (hasNewParameterValue && hasCurrentParameterValue) { if (!RoutePartsEqual(currentParameterValue, newParameterValue)) { // Stop copying current values when we find one that doesn't match return false; } } // If the parameter is a match, add it to the list of values we will use for URI generation if (hasNewParameterValue) { if (IsRoutePartNonEmpty(newParameterValue)) { acceptedValues.Add(parameterName, newParameterValue); } } else { if (hasCurrentParameterValue) { acceptedValues.Add(parameterName, currentParameterValue); } } return true; }); // Add all remaining new values to the list of values we will use for URI generation foreach (var newValue in values) { if (IsRoutePartNonEmpty(newValue.Value)) { if (!acceptedValues.ContainsKey(newValue.Key)) { acceptedValues.Add(newValue.Key, newValue.Value); } } } // Add all current values that aren't in the URI at all foreach (var currentValue in currentValues) { string parameterName = currentValue.Key; if (!acceptedValues.ContainsKey(parameterName)) { PathParameterSubsegment parameterSubsegment = GetParameterSubsegment(_pathSegments, parameterName); if (parameterSubsegment == null) { acceptedValues.Add(parameterName, currentValue.Value); } } } // Add all remaining default values from the route to the list of values we will use for URI generation ForEachParameter(_pathSegments, delegate(PathParameterSubsegment parameterSubsegment) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { object defaultValue; if (!IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { // Add the default value only if there isn't already a new value for it and // only if it actually has a default value, which we determine based on whether // the parameter value is required. acceptedValues.Add(parameterSubsegment.ParameterName, defaultValue); } } return true; }); // All required parameters in this URI must have values from somewhere (i.e. the accepted values) bool hasAllRequiredValues = ForEachParameter(_pathSegments, delegate(PathParameterSubsegment parameterSubsegment) { object defaultValue; if (IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { // If the route parameter value is required that means there's // no default value, so if there wasn't a new value for it // either, this route won't match. return false; } } return true; }); if (!hasAllRequiredValues) { return null; } // All other default values must match if they are explicitly defined in the new values HttpRouteValueDictionary otherDefaultValues = new HttpRouteValueDictionary(defaultValues); ForEachParameter(_pathSegments, delegate(PathParameterSubsegment parameterSubsegment) { otherDefaultValues.Remove(parameterSubsegment.ParameterName); return true; }); foreach (var defaultValue in otherDefaultValues) { object value; if (values.TryGetValue(defaultValue.Key, out value)) { unusedNewValues.Remove(defaultValue.Key); if (!RoutePartsEqual(value, defaultValue.Value)) { // If there is a non-parameterized value in the route and there is a // new value for it and it doesn't match, this route won't match. return null; } } } // Step 2: If the route is a match generate the appropriate URI StringBuilder uri = new StringBuilder(); StringBuilder pendingParts = new StringBuilder(); bool pendingPartsAreAllSafe = false; bool blockAllUriAppends = false; for (int i = 0; i < _pathSegments.Count; i++) { PathSegment pathSegment = _pathSegments[i]; // parsedRouteUriPart if (pathSegment is PathSeparatorSegment) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } pendingPartsAreAllSafe = false; // Guard against appending multiple separators for empty segments if (pendingParts.Length > 0 && pendingParts[pendingParts.Length - 1] == '/') { // Dev10 676725: Route should not be matched if that causes mismatched tokens // Dev11 86819: We will allow empty matches if all subsequent segments are null if (blockAllUriAppends) { return null; } // Append any pending literals to the URI (without the trailing slash) and prevent any future appends uri.Append(pendingParts.ToString(0, pendingParts.Length - 1)); pendingParts.Length = 0; blockAllUriAppends = true; } else { pendingParts.Append("/"); } } else { PathContentSegment contentPathSegment = pathSegment as PathContentSegment; if (contentPathSegment != null) { // Segments are treated as all-or-none. We should never output a partial segment. // If we add any subsegment of this segment to the generated URI, we have to add // the complete match. For example, if the subsegment is "{p1}-{p2}.xml" and we // used a value for {p1}, we have to output the entire segment up to the next "/". // Otherwise we could end up with the partial segment "v1" instead of the entire // segment "v1-v2.xml". bool addedAnySubsegments = false; foreach (PathSubsegment subsegment in contentPathSegment.Subsegments) { PathLiteralSubsegment literalSubsegment = subsegment as PathLiteralSubsegment; if (literalSubsegment != null) { // If it's a literal we hold on to it until we are sure we need to add it pendingPartsAreAllSafe = true; pendingParts.Append(literalSubsegment.Literal); } else { PathParameterSubsegment parameterSubsegment = subsegment as PathParameterSubsegment; if (parameterSubsegment != null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; addedAnySubsegments = true; } } pendingPartsAreAllSafe = false; // If it's a parameter, get its value object acceptedParameterValue; bool hasAcceptedParameterValue = acceptedValues.TryGetValue(parameterSubsegment.ParameterName, out acceptedParameterValue); if (hasAcceptedParameterValue) { unusedNewValues.Remove(parameterSubsegment.ParameterName); } object defaultParameterValue; defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultParameterValue); if (RoutePartsEqual(acceptedParameterValue, defaultParameterValue)) { // If the accepted value is the same as the default value, mark it as pending since // we won't necessarily add it to the URI we generate. pendingParts.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); } else { if (blockAllUriAppends) { return null; } // Add the new part to the URI as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } uri.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); addedAnySubsegments = true; } } else { Contract.Assert(false, "Invalid path subsegment type"); } } } if (addedAnySubsegments) { // See comment above about why we add the pending parts if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } else { Contract.Assert(false, "Invalid path segment type"); } } } if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return null; } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); } } // Process constraints keys if (constraints != null) { // If there are any constraints, mark all the keys as being used so that we don't // generate query string items for custom constraints that don't appear as parameters // in the URI format. foreach (var constraintsItem in constraints) { unusedNewValues.Remove(constraintsItem.Key); } } // Encode the URI before we append the query string, otherwise we would double encode the query string StringBuilder encodedUri = new StringBuilder(); encodedUri.Append(UriEncode(uri.ToString())); uri = encodedUri; // Add remaining new values as query string parameters to the URI if (unusedNewValues.Count > 0) { // Generate the query string bool firstParam = true; foreach (string unusedNewValue in unusedNewValues) { object value; if (acceptedValues.TryGetValue(unusedNewValue, out value)) { uri.Append(firstParam ? '?' : '&'); firstParam = false; uri.Append(Uri.EscapeDataString(unusedNewValue)); uri.Append('='); uri.Append(Uri.EscapeDataString(Convert.ToString(value, CultureInfo.InvariantCulture))); } } } return new BoundRouteTemplate { BoundTemplate = uri.ToString(), Values = acceptedValues }; }
public SwaggerBoundRouteTemplate Bind(IDictionary <string, object> currentValues, IDictionary <string, object> values, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary constraints) { if (currentValues == null) { currentValues = new HttpRouteValueDictionary(); } if (values == null) { values = new HttpRouteValueDictionary(); } if (defaultValues == null) { defaultValues = new HttpRouteValueDictionary(); } // The set of values we should be using when generating the URI in this route HttpRouteValueDictionary acceptedValues = new HttpRouteValueDictionary(); // Keep track of which new values have been used HashSet <string> unusedNewValues = new HashSet <string>(values.Keys, StringComparer.OrdinalIgnoreCase); // Step 1: Get the list of values we're going to try to use to match and generate this URI // Find out which entries in the URI are valid for the URI we want to generate. // If the URI had ordered parameters a="1", b="2", c="3" and the new values // specified that b="9", then we need to invalidate everything after it. The new // values should then be a="1", b="9", c=<no value>. ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment parameterSubsegment) { // If it's a parameter subsegment, examine the current value to see if it matches the new value string parameterName = parameterSubsegment.ParameterName; object newParameterValue; bool hasNewParameterValue = values.TryGetValue(parameterName, out newParameterValue); if (hasNewParameterValue) { unusedNewValues.Remove(parameterName); } object currentParameterValue; bool hasCurrentParameterValue = currentValues.TryGetValue(parameterName, out currentParameterValue); if (hasNewParameterValue && hasCurrentParameterValue) { if (!RoutePartsEqual(currentParameterValue, newParameterValue)) { // Stop copying current values when we find one that doesn't match return(false); } } // If the parameter is a match, add it to the list of values we will use for URI generation if (hasNewParameterValue) { if (IsRoutePartNonEmpty(newParameterValue)) { acceptedValues.Add(parameterName, newParameterValue); } } else { if (hasCurrentParameterValue) { acceptedValues.Add(parameterName, currentParameterValue); } } return(true); }); // Add all remaining new values to the list of values we will use for URI generation foreach (var newValue in values) { if (IsRoutePartNonEmpty(newValue.Value)) { if (!acceptedValues.ContainsKey(newValue.Key)) { acceptedValues.Add(newValue.Key, newValue.Value); } } } // Add all current values that aren't in the URI at all foreach (var currentValue in currentValues) { string parameterName = currentValue.Key; if (!acceptedValues.ContainsKey(parameterName)) { SwaggerPathParameterSubSegment parameterSubsegment = GetParameterSubsegment(_pathSegments, parameterName); if (parameterSubsegment == null) { acceptedValues.Add(parameterName, currentValue.Value); } } } // Add all remaining default values from the route to the list of values we will use for URI generation ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment parameterSubsegment) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { object defaultValue; if (!IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { // Add the default value only if there isn't already a new value for it and // only if it actually has a default value, which we determine based on whether // the parameter value is required. acceptedValues.Add(parameterSubsegment.ParameterName, defaultValue); } } return(true); }); // All required parameters in this URI must have values from somewhere (i.e. the accepted values) bool hasAllRequiredValues = ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment parameterSubsegment) { object defaultValue; if (IsParameterRequired(parameterSubsegment, defaultValues, out defaultValue)) { if (!acceptedValues.ContainsKey(parameterSubsegment.ParameterName)) { // If the route parameter value is required that means there's // no default value, so if there wasn't a new value for it // either, this route won't match. return(false); } } return(true); }); if (!hasAllRequiredValues) { return(null); } // All other default values must match if they are explicitly defined in the new values HttpRouteValueDictionary otherDefaultValues = new HttpRouteValueDictionary(defaultValues); ForEachParameter(_pathSegments, delegate(SwaggerPathParameterSubSegment parameterSubsegment) { otherDefaultValues.Remove(parameterSubsegment.ParameterName); return(true); }); foreach (var defaultValue in otherDefaultValues) { object value; if (values.TryGetValue(defaultValue.Key, out value)) { unusedNewValues.Remove(defaultValue.Key); if (!RoutePartsEqual(value, defaultValue.Value)) { // If there is a non-parameterized value in the route and there is a // new value for it and it doesn't match, this route won't match. return(null); } } } // Step 2: If the route is a match generate the appropriate URI StringBuilder uri = new StringBuilder(); StringBuilder pendingParts = new StringBuilder(); bool pendingPartsAreAllSafe = false; bool blockAllUriAppends = false; for (int i = 0; i < _pathSegments.Count; i++) { SwaggerPathSegment pathSegment = _pathSegments[i]; // parsedRouteUriPart if (pathSegment is SwaggerPathSeparatorSegment) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } pendingPartsAreAllSafe = false; // Guard against appending multiple separators for empty segments if (pendingParts.Length > 0 && pendingParts[pendingParts.Length - 1] == '/') { // Dev10 676725: Route should not be matched if that causes mismatched tokens // Dev11 86819: We will allow empty matches if all subsequent segments are null if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI (without the trailing slash) and prevent any future appends uri.Append(pendingParts.ToString(0, pendingParts.Length - 1)); pendingParts.Length = 0; blockAllUriAppends = true; } else { pendingParts.Append("/"); } } else { SwaggerPathContentSegment contentPathSegment = pathSegment as SwaggerPathContentSegment; if (contentPathSegment != null) { // Segments are treated as all-or-none. We should never output a partial segment. // If we add any subsegment of this segment to the generated URI, we have to add // the complete match. For example, if the subsegment is "{p1}-{p2}.xml" and we // used a value for {p1}, we have to output the entire segment up to the next "/". // Otherwise we could end up with the partial segment "v1" instead of the entire // segment "v1-v2.xml". bool addedAnySubsegments = false; foreach (SwaggerPathSubsegment subsegment in contentPathSegment.Subsegments) { SwaggerPathLiteralSubsegment literalSubsegment = subsegment as SwaggerPathLiteralSubsegment; if (literalSubsegment != null) { // If it's a literal we hold on to it until we are sure we need to add it pendingPartsAreAllSafe = true; pendingParts.Append(literalSubsegment.Literal); } else { SwaggerPathParameterSubSegment parameterSubsegment = subsegment as SwaggerPathParameterSubSegment; if (parameterSubsegment != null) { if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; addedAnySubsegments = true; } } pendingPartsAreAllSafe = false; // If it's a parameter, get its value object acceptedParameterValue; bool hasAcceptedParameterValue = acceptedValues.TryGetValue(parameterSubsegment.ParameterName, out acceptedParameterValue); if (hasAcceptedParameterValue) { unusedNewValues.Remove(parameterSubsegment.ParameterName); } object defaultParameterValue; defaultValues.TryGetValue(parameterSubsegment.ParameterName, out defaultParameterValue); if (RoutePartsEqual(acceptedParameterValue, defaultParameterValue)) { // If the accepted value is the same as the default value, mark it as pending since // we won't necessarily add it to the URI we generate. pendingParts.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); } else { if (blockAllUriAppends) { return(null); } // Add the new part to the URI as well as any pending parts if (pendingParts.Length > 0) { // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } uri.Append(Convert.ToString(acceptedParameterValue, CultureInfo.InvariantCulture)); addedAnySubsegments = true; } } else { Contract.Assert(false, "Invalid path subsegment type"); } } } if (addedAnySubsegments) { // See comment above about why we add the pending parts if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); pendingParts.Length = 0; } } } else { Contract.Assert(false, "Invalid path segment type"); } } } if (pendingPartsAreAllSafe) { // Accept if (pendingParts.Length > 0) { if (blockAllUriAppends) { return(null); } // Append any pending literals to the URI uri.Append(pendingParts.ToString()); } } // Process constraints keys if (constraints != null) { // If there are any constraints, mark all the keys as being used so that we don't // generate query string items for custom constraints that don't appear as parameters // in the URI format. foreach (var constraintsItem in constraints) { unusedNewValues.Remove(constraintsItem.Key); } } // Encode the URI before we append the query string, otherwise we would double encode the query string StringBuilder encodedUri = new StringBuilder(); encodedUri.Append(UriEncode(uri.ToString())); uri = encodedUri; // Add remaining new values as query string parameters to the URI if (unusedNewValues.Count > 0) { // Generate the query string bool firstParam = true; foreach (string unusedNewValue in unusedNewValues) { object value; if (acceptedValues.TryGetValue(unusedNewValue, out value)) { uri.Append(firstParam ? '?' : '&'); firstParam = false; uri.Append(Uri.EscapeDataString(unusedNewValue)); uri.Append('='); uri.Append(Uri.EscapeDataString(Convert.ToString(value, CultureInfo.InvariantCulture))); } } } return(new SwaggerBoundRouteTemplate { BoundTemplate = uri.ToString(), Values = acceptedValues }); }
private static void MatchCatchAll(PathContentSegment contentPathSegment, IEnumerable<string> remainingRequestSegments, HttpRouteValueDictionary defaultValues, HttpRouteValueDictionary matchedValues) { string remainingRequest = String.Join(String.Empty, remainingRequestSegments.ToArray()); PathParameterSubsegment catchAllSegment = contentPathSegment.Subsegments[0] as PathParameterSubsegment; object catchAllValue; if (remainingRequest.Length > 0) { catchAllValue = remainingRequest; } else { defaultValues.TryGetValue(catchAllSegment.ParameterName, out catchAllValue); } matchedValues.Add(catchAllSegment.ParameterName, catchAllValue); }
private static bool MatchSingleContentPathSegment(PathSubsegment pathSubsegment, string requestPathSegment, HttpRouteValueDictionary matchedValues) { PathParameterSubsegment parameterSubsegment = pathSubsegment as PathParameterSubsegment; if (parameterSubsegment == null) { // Handle a single literal segment PathLiteralSubsegment literalSubsegment = pathSubsegment as PathLiteralSubsegment; Contract.Assert(literalSubsegment != null, "Invalid path segment type"); return literalSubsegment.Literal.Equals(requestPathSegment, StringComparison.OrdinalIgnoreCase); } else { // Handle a single parameter segment matchedValues.Add(parameterSubsegment.ParameterName, requestPathSegment); return true; } }
/// <summary> /// Builds the contents for the route table based on the attributes /// found on a specific controller type. /// </summary> /// <param name="routes"></param> /// <param name="controllerType"></param> private static void BuildControllerRoutes(HttpRouteCollection routes, Type controllerType) { var attributes = (HttpRouteAttribute[]) controllerType.GetCustomAttributes(typeof(HttpRouteAttribute), true); string controller = controllerType.Name; // Translate the somewhat weird controller name into one the routing system // understands, by removing the Controller part from the name. if (controller.EndsWith("Controller", StringComparison.Ordinal)) { controller = controller.Substring(0, controller.IndexOf("Controller")); } foreach (var attribute in attributes) { var routeValuesDictionary = new HttpRouteValueDictionary(); routeValuesDictionary.Add("controller", controller); // Create the route and attach the default route handler to it. var route = new HttpRoute(attribute.UriTemplate, routeValuesDictionary, new HttpRouteValueDictionary(), new HttpRouteValueDictionary()); routes.Add(Guid.NewGuid().ToString(), route); } }
public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { var apiControllerAssembly = request.GetOwinEnvironment()["ApiControllersAssembly"].ToString(); Logger.Debug($"{nameof(CustomHttpControllerSelector)}: {{{nameof(apiControllerAssembly)}: {apiControllerAssembly}}}"); var routeData = request.GetRouteData(); var routeCollectionRoute = routeData.Route as IReadOnlyCollection <IHttpRoute>; var newRoutes = new List <IHttpRoute>(); var newRouteCollectionRoute = new RouteCollectionRoute(); foreach (var route in routeCollectionRoute) { var filteredDataTokens = FilterDataTokens(route, apiControllerAssembly); if (filteredDataTokens.Count == 2) { var newRoute = new HttpRoute(route.RouteTemplate, (HttpRouteValueDictionary)route.Defaults, (HttpRouteValueDictionary)route.Constraints, filteredDataTokens); newRoutes.Add(newRoute); } } var newRouteDataValues = new HttpRouteValueDictionary(); foreach (var routeDataKvp in routeData.Values) { var newRouteDataCollection = new List <IHttpRouteData>(); var routeDataCollection = routeDataKvp.Value as IEnumerable <IHttpRouteData>; if (routeDataCollection != null) { foreach (var innerRouteData in routeDataCollection) { var filteredDataTokens = FilterDataTokens(innerRouteData.Route, apiControllerAssembly); if (filteredDataTokens.Count == 2) { var newInnerRoute = new HttpRoute(innerRouteData.Route.RouteTemplate, (HttpRouteValueDictionary)innerRouteData.Route.Defaults, (HttpRouteValueDictionary)innerRouteData.Route.Constraints, filteredDataTokens); var newInnerRouteData = new HttpRouteData(newInnerRoute, (HttpRouteValueDictionary)innerRouteData.Values); newRouteDataCollection.Add(newInnerRouteData); } } newRouteDataValues.Add(routeDataKvp.Key, newRouteDataCollection); } else { newRouteDataValues.Add(routeDataKvp.Key, routeDataKvp.Value); } HttpRouteData newRouteData; if (newRoutes.Count > 1) { newRouteCollectionRoute.EnsureInitialized(() => newRoutes); newRouteData = new HttpRouteData(newRouteCollectionRoute, newRouteDataValues); } else { newRouteData = new HttpRouteData(newRoutes[0], newRouteDataValues); } request.SetRouteData(newRouteData); } var controllerDescriptor = base.SelectController(request); return(controllerDescriptor); }