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());
            }
        }
예제 #4
0
        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);
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
    }
예제 #11
0
        //
        // 摘要:
        //     通过查找路由的 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);
        }
예제 #12
0
        //
        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);
        }
예제 #13
0
        /// <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);
                }
            }
        }
예제 #14
0
    // 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);
        }
예제 #16
0
        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);
        }
예제 #20
0
        /// <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);
        }
예제 #25
0
        //
        // 摘要:
        //     映射指定的路由模板并设置默认的路由值、约束和终结点消息处理程序。
        //
        // 参数:
        //   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);
        }
예제 #27
0
        /// <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;
        }
예제 #28
0
        /// <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);
        }
예제 #31
0
        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);
                }
            }
        }
예제 #33
0
        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);
        }
예제 #36
0
        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}}");
        }
예제 #37
0
        /// <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));
        }
예제 #41
0
 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);
                }
            }
        }
예제 #43
0
 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);
        }
예제 #48
0
 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);
        }