Exemplo n.º 1
0
        public async Task ApplyAsync_PrefersEndpointsWithODataRoutingMetadata()
        {
            // Arrange
            IEdmModel             model           = EdmCoreModel.Instance;
            IODataRoutingMetadata routingMetadata = new ODataRoutingMetadata("odata", model, new ODataPathTemplate());

            Endpoint[] endpoints = new[]
            {
                CreateEndpoint("/", routingMetadata, new HttpMethodMetadata(new[] { "get" })),
                CreateEndpoint("/", routingMetadata, new HttpMethodMetadata(new[] { "post" })),
                CreateEndpoint("/", routingMetadata, new HttpMethodMetadata(new[] { "delete" }))
            };

            CandidateSet candidateSet = CreateCandidateSet(endpoints);

            HttpContext httpContext = CreateHttpContext("POST");

            HttpMethodMatcherPolicy httpMethodPolicy = new HttpMethodMatcherPolicy();

            ODataRoutingMatcherPolicy policy = CreatePolicy();

            // Act
            await httpMethodPolicy.ApplyAsync(httpContext, candidateSet);

            await policy.ApplyAsync(httpContext, candidateSet);

            // Assert
            Assert.False(candidateSet.IsValidCandidate(0));
            Assert.True(candidateSet.IsValidCandidate(1));
            Assert.False(candidateSet.IsValidCandidate(2));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethods">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethods, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (string.IsNullOrEmpty(httpMethods))
            {
                throw Error.ArgumentNullOrEmpty(nameof(httpMethods));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            string[] methods = httpMethods.Split(',');
            foreach (string template in path.GetTemplates(options))
            {
                // Be noted: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ActionAttributeRouteModel.cs#L74-L75
                // No matter whether the action selector model is absolute route template, the controller's attribute will apply automatically
                // So, let's only create/update the action selector model
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    // Create a new selector model.
                    selectorModel = CreateSelectorModel(action, methods);
                    action.Selectors.Add(selectorModel);
                }
                else
                {
                    // Update the existing non attribute routing selector model.
                    selectorModel = UpdateSelectorModel(selectorModel, methods);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel
                {
                    // OData convention route template doesn't get combined with the route template applied to the controller.
                    // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                    Template = $"/{templateStr}",
                    Name     = templateStr // do we need this?
                };

                // Check with .NET Team whether the "Endpoint name metadata" needed?
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString())); // Do we need this?
            }
        }
Exemplo n.º 3
0
        private static void AddHttpMethod(ODataRoutingMetadata metadata, string httpMethod)
        {
            if (string.IsNullOrEmpty(httpMethod))
            {
                return;
            }

            string[] methods = httpMethod.Split(',');
            foreach (var method in methods)
            {
                metadata.HttpMethods.Add(method);
            }
        }
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            foreach ((string template, string display) in path.GetTemplates(options))
            {
                // We have to check the selector model on controller?
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path)
                {
                    TemplateDisplayName = string.IsNullOrEmpty(prefix) ? display : $"{prefix}/{display}"
                };

                AddHttpMethod(odataMetadata, httpMethod);

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                // OData convention route template doesn't get combined with the route template applied to the controller.
                // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                templateStr = "/" + templateStr;

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
        public void Ctor_SetPropertiesCorrectly()
        {
            // Assert
            IEdmModel         model = EdmCoreModel.Instance;
            ODataPathTemplate path  = new ODataPathTemplate();

            // & Act & Assert
            ODataRoutingMetadata metadata = new ODataRoutingMetadata("prefix", model, path);

            // Assert
            Assert.Equal("prefix", metadata.Prefix);
            Assert.Same(model, metadata.Model);
            Assert.Same(path, metadata.Template);
        }
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            foreach ((string template, string display) in path.GetTemplates(options))
            {
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path)
                {
                    TemplateDisplayName = string.IsNullOrEmpty(prefix) ? display : $"{prefix}/{display}"
                };

                AddHttpMethod(odataMetadata, httpMethod);

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        public static void AddSelector(this ActionModel action, string prefix, IEdmModel model, ODataPathTemplate path)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw new ArgumentNullException(nameof(path));
            }

            var httpMethods = action.GetSupportedHttpMethods();

            foreach (var template in path.GetTemplates())
            {
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = new SelectorModel();
                    action.Selectors.Add(selectorModel);
                }

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                // Check with .NET Team whether the "Endpoint name metadata"
                // selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(templateStr));
                foreach (var httpMethod in httpMethods)
                {
                    odataMetadata.HttpMethods.Add(httpMethod);
                }
            }
        }
Exemplo n.º 8
0
        public void AppliesToEndpoints_EndpointHasODataRoutingMetadata_ReturnsTrue()
        {
            // Arrange
            IODataRoutingMetadata routingMetadata = new ODataRoutingMetadata();

            Endpoint[] endpoints = new[]
            {
                CreateEndpoint("/", routingMetadata),
                CreateEndpoint("/", null),
            };

            ODataRoutingMatcherPolicy policy = CreatePolicy();

            // Act
            bool result = policy.AppliesToEndpoints(endpoints);

            // Assert
            Assert.True(result);
        }
Exemplo n.º 9
0
        private SelectorModel CreateActionSelectorModel(string prefix, IEdmModel model, IServiceProvider sp,
                                                        string routeTemplate, SelectorModel actionSelectorModel,
                                                        string originalTemplate, string actionName, string controllerName)
        {
            try
            {
                // Do the uri parser, it will throw exception if the route template is not a OData path.
                ODataPathTemplate pathTemplate = _templateParser.Parse(model, routeTemplate, sp);
                if (pathTemplate != null)
                {
                    // Create a new selector model?
                    SelectorModel newSelectorModel = new SelectorModel(actionSelectorModel);
                    // Shall we remove any certain attributes/metadata?
                    ClearMetadata(newSelectorModel);

                    // Add OData routing metadata
                    ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, pathTemplate);
                    newSelectorModel.EndpointMetadata.Add(odataMetadata);

                    // replace the attribute routing template using absolute routing template to avoid appending any controller route template
                    newSelectorModel.AttributeRouteModel = new AttributeRouteModel()
                    {
                        Template = $"/{originalTemplate}" // add a "/" to make sure it's absolute template, don't combine with controller
                    };

                    return(newSelectorModel);
                }

                return(null);
            }
            catch (ODataException ex)
            {
                // use the logger to log the wrong odata attribute template. Shall we log the others?
                string warning = string.Format(CultureInfo.CurrentCulture, SRResources.InvalidODataRouteOnAction,
                                               originalTemplate, actionName, controllerName, ex.Message);

                // Whether we throw exception or mark it as warning is a design pattern.
                // throw new ODataException(warning);
                _logger.LogWarning(warning);
                return(null);
            }
        }
Exemplo n.º 10
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethods">The supported http methods, if multiple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethods, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (string.IsNullOrEmpty(httpMethods))
            {
                throw Error.ArgumentNullOrEmpty(nameof(httpMethods));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            // if the controller has attribute route decorated, for example:
            // [Route("api/[controller]")]
            // public class CustomersController : Controller
            // {}
            // let's always create new selector model for action.
            // Since the new created selector model is absolute attribute route, the controller attribute route doesn't apply to this selector model.
            bool hasAttributeRouteOnController = action.Controller.Selectors.Any(s => s.AttributeRouteModel != null);

            // If the methods have different case sensitive, for example, "get", "Get", in the ASP.NET Core 3.1,
            // It will throw "An item with the same key has already been added. Key: GET", in
            // HttpMethodMatcherPolicy.BuildJumpTable(Int32 exitDestination, IReadOnlyList`1 edges)
            // Another root cause is that in attribute routing, we reuse the HttpMethodMetadata, the method name is always "upper" case.
            // Therefore, we upper the http method name always.
            string[] methods = httpMethods.ToUpperInvariant().Split(',');
            foreach (string template in path.GetTemplates(options))
            {
                // Be noted: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ActionAttributeRouteModel.cs#L74-L75
                // No matter whether the action selector model is absolute route template, the controller's attribute will apply automatically
                // So, let's only create/update the action selector model
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (hasAttributeRouteOnController || selectorModel == null)
                {
                    // Create a new selector model.
                    selectorModel = CreateSelectorModel(action, methods);
                    action.Selectors.Add(selectorModel);
                }
                else
                {
                    // Update the existing non attribute routing selector model.
                    selectorModel = UpdateSelectorModel(selectorModel, methods);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel
                {
                    // OData convention route template doesn't get combined with the route template applied to the controller.
                    // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                    Template = $"/{templateStr}",
                    Name     = templateStr // do we need this?
                };

                // Check with .NET Team whether the "Endpoint name metadata" needed?
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString())); // Do we need this?
            }
        }