private static bool IsSupportedRequestMethod(ODataRequestMethod method)
 {
     return(ODataRequestMethod.Delete == method ||
            ODataRequestMethod.Put == method ||
            ODataRequestMethod.Post == method ||
            ODataRequestMethod.Get == method);
 }
示例#2
0
        private static string GetActionNamePrefix(ODataRequestMethod method)
        {
            string actionNamePrefix;

            switch (method)
            {
            case ODataRequestMethod.Get:
                actionNamePrefix = "Get";
                break;

            case ODataRequestMethod.Put:
                actionNamePrefix = "Put";
                break;

            case ODataRequestMethod.Patch:
            case ODataRequestMethod.Merge:
                actionNamePrefix = "Patch";
                break;

            default:
                return(null);
            }

            return(actionNamePrefix);
        }
        /// <inheritdoc/>
        internal static string SelectActionImpl(ODataPath odataPath, IWebApiControllerContext controllerContext, IWebApiActionMap actionMap)
        {
            ODataRequestMethod requestMethod = controllerContext.Request.Method;

            if (!IsSupportedRequestMethod(requestMethod))
            {
                return(null);
            }

            if (odataPath.PathTemplate == "~/entityset/key/navigation/$ref" ||
                odataPath.PathTemplate == "~/entityset/key/cast/navigation/$ref" ||
                odataPath.PathTemplate == "~/singleton/navigation/$ref" ||
                odataPath.PathTemplate == "~/singleton/cast/navigation/$ref")
            {
                NavigationPropertyLinkSegment navigationLinkSegment = (NavigationPropertyLinkSegment)odataPath.Segments.Last();
                IEdmNavigationProperty        navigationProperty    = navigationLinkSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        controllerContext.AddKeyValueToRouteData((KeySegment)odataPath.Segments[1]);
                    }

                    controllerContext.RouteData.Add(ODataRouteConstants.NavigationProperty, navigationLinkSegment.NavigationProperty.Name);
                    return(refActionName);
                }
            }
            else if ((ODataRequestMethod.Delete == requestMethod) && (
                         odataPath.PathTemplate == "~/entityset/key/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/entityset/key/cast/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/singleton/navigation/key/$ref" ||
                         odataPath.PathTemplate == "~/singleton/cast/navigation/key/$ref"))
            {
                // the second key segment is the last segment in the path.
                // So the previous of last segment is the navigation property link segment.
                NavigationPropertyLinkSegment navigationLinkSegment = (NavigationPropertyLinkSegment)odataPath.Segments[odataPath.Segments.Count - 2];
                IEdmNavigationProperty        navigationProperty    = navigationLinkSegment.NavigationProperty;
                IEdmEntityType declaringType = navigationProperty.DeclaringEntityType();

                string refActionName = FindRefActionName(actionMap, navigationProperty, declaringType, requestMethod);
                if (refActionName != null)
                {
                    if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                    {
                        controllerContext.AddKeyValueToRouteData((KeySegment)odataPath.Segments[1]);
                    }

                    controllerContext.RouteData.Add(ODataRouteConstants.NavigationProperty, navigationLinkSegment.NavigationProperty.Name);
                    controllerContext.AddKeyValueToRouteData((KeySegment)odataPath.Segments.Last(e => e is KeySegment), ODataRouteConstants.RelatedKey);
                    return(refActionName);
                }
            }

            return(null);
        }
        /// <summary>
        /// Selects the action for OData requests.
        /// </summary>
        /// <param name="odataPath">The OData path.</param>
        /// <param name="controllerContext">The controller context.</param>
        /// <param name="actionMap">The action map.</param>
        /// <returns>
        ///   <c>null</c> if the request isn't handled by this convention; otherwise, the name of the selected action
        /// </returns>
        internal static string SelectActionImpl(ODataPath odataPath, IWebApiControllerContext controllerContext, IWebApiActionMap actionMap)
        {
            ODataRequestMethod method = controllerContext.Request.GetRequestMethodOrPreflightMethod();

            if (method != ODataRequestMethod.Get)
            {
                // [EnableNestedPaths] only supports GET requests
                return(null);
            }

            // unsupported path segments
            if (odataPath.PathTemplate.EndsWith("$ref"))
            {
                return(null);
            }

            ODataPathSegment firstSegment = odataPath.Segments.FirstOrDefault();

            string sourceName;

            if (firstSegment is EntitySetSegment entitySetSegment)
            {
                sourceName = entitySetSegment.EntitySet.Name;
            }
            else if (firstSegment is SingletonSegment singletonSegment)
            {
                sourceName = singletonSegment.Singleton.Name;
            }
            else
            {
                // this only supports paths starting with an entity set or singleton
                return(null);
            }

            // if we did not find a matching action amongst the conventional user-defined methods
            // then let's check if the controller has a Get method with [EnableNestedPaths] attribute
            // which should be used to catch any nested GET request
            string action = actionMap.FindMatchingAction("Get" + sourceName, "Get");

            if (action == null)
            {
                return(null);
            }

            IWebApiActionDescriptor descriptor = actionMap.GetActionDescriptor(action);

            if (descriptor == null)
            {
                return(null);
            }

            if (!descriptor.GetCustomAttributes <EnableNestedPathsAttribute>(/* inherit */ true).Any())
            {
                return(null);
            }

            return(descriptor.ActionName);
        }
示例#5
0
        /// <summary>
        /// Determine if the Http method is a match.
        /// </summary>
        public bool IsHttpMethodSupported(ODataRequestMethod method)
        {
            if (this.supportedHttpMethods == null)
            {
                // Assume all methods are supported if not specified.
                return(true);
            }

            return(this.supportedHttpMethods.Contains(method));
        }
        private static string GetActionMethodPrefix(ODataRequestMethod method)
        {
            switch (method)
            {
            case ODataRequestMethod.Get:
                return("Get");

            case ODataRequestMethod.Post:
                return("PostTo");

            case ODataRequestMethod.Put:
                return("PutTo");

            case ODataRequestMethod.Patch:
                return("PatchTo");

            default:
                return(null);
            }
        }
        /// <summary>
        /// Initializes a new instance of the WebApiActionDescriptor class.
        /// </summary>
        /// <param name="actionDescriptor">The inner descriptor.</param>
        public WebApiActionDescriptor(ControllerActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
            {
                throw Error.ArgumentNull("actionDescriptor");
            }

            this.innerDescriptor      = actionDescriptor;
            this.supportedHttpMethods = new List <ODataRequestMethod>();

            // Determine the supported methods.
            IEnumerable <string> actionMethods = null; /*actionDescriptor.ActionConstraints?
                                                        * .OfType<HttpMethodActionConstraint>()
                                                        * .FirstOrDefault()?
                                                        * .HttpMethods;
                                                        */

            if (actionMethods == null)
            {
                // If no HttpMethodActionConstraint is specified, fall back to convention the way AspNet does.
                actionMethods = SupportedHttpMethodConventions
                                .Where(method => actionDescriptor.MethodInfo.Name.StartsWith(method, StringComparison.OrdinalIgnoreCase));

                // Use POST as the default method.
                if (!actionMethods.Any())
                {
                    actionMethods = new string[] { "POST" };
                }
            }

            foreach (string method in actionMethods)
            {
                bool ignoreCase = true;
                ODataRequestMethod methodEnum = ODataRequestMethod.Unknown;
                if (Enum.TryParse <ODataRequestMethod>(method, ignoreCase, out methodEnum))
                {
                    this.supportedHttpMethods.Add(methodEnum);
                }
            }
        }
示例#8
0
        /// <summary>
        /// Initializes a new instance of the <see cref="WebApiActionDescriptor"/> class.
        /// </summary>
        /// <param name="actionDescriptor">The inner descriptor.</param>
        public WebApiActionDescriptor(HttpActionDescriptor actionDescriptor)
        {
            if (actionDescriptor == null)
            {
                throw Error.ArgumentNull("actionDescriptor");
            }

            this.innerDescriptor = actionDescriptor;

            if (actionDescriptor.SupportedHttpMethods != null)
            {
                this.supportedHttpMethods = new List <ODataRequestMethod>();
                foreach (HttpMethod method in actionDescriptor.SupportedHttpMethods)
                {
                    bool ignoreCase = true;
                    ODataRequestMethod methodEnum = ODataRequestMethod.Unknown;
                    if (Enum.TryParse <ODataRequestMethod>(method.Method, ignoreCase, out methodEnum))
                    {
                        this.supportedHttpMethods.Add(methodEnum);
                    }
                }
            }
        }
        private static string FindRefActionName(IWebApiActionMap actionMap,
                                                IEdmNavigationProperty navigationProperty, IEdmEntityType declaringType, ODataRequestMethod method)
        {
            string actionNamePrefix;

            switch (method)
            {
            case ODataRequestMethod.Delete:
                actionNamePrefix = DeleteRefActionNamePrefix;
                break;

            case ODataRequestMethod.Get:
                actionNamePrefix = GetRefActionNamePrefix;
                break;

            default:
                actionNamePrefix = CreateRefActionNamePrefix;
                break;
            }

            // Examples: CreateRefToOrdersFromCustomer, CreateRefToOrders, CreateRef.
            return(actionMap.FindMatchingAction(
                       actionNamePrefix + "To" + navigationProperty.Name + "From" + declaringType.Name,
                       actionNamePrefix + "To" + navigationProperty.Name,
                       actionNamePrefix));
        }
        private static IEdmProperty GetProperty(ODataPath odataPath, ODataRequestMethod method, out string prefix,
                                                out TypeSegment cast)
        {
            prefix = String.Empty;
            cast   = null;
            PropertySegment segment = null;

            if (odataPath.PathTemplate == "~/entityset/key/property" ||
                odataPath.PathTemplate == "~/entityset/key/cast/property" ||
                odataPath.PathTemplate == "~/singleton/property" ||
                odataPath.PathTemplate == "~/singleton/cast/property")
            {
                PropertySegment tempSegment =
                    (PropertySegment)odataPath.Segments[odataPath.Segments.Count - 1];

                switch (method)
                {
                case ODataRequestMethod.Get:
                    prefix  = "Get";
                    segment = tempSegment;
                    break;

                case ODataRequestMethod.Post:
                    //Allow post only to collection properties
                    if (tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PostTo";
                        segment = tempSegment;
                    }
                    break;

                case ODataRequestMethod.Put:
                    prefix  = "PutTo";
                    segment = tempSegment;
                    break;

                case ODataRequestMethod.Patch:
                    // OData Spec: PATCH is not supported for collection properties.
                    if (!tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PatchTo";
                        segment = tempSegment;
                    }
                    break;

                case ODataRequestMethod.Delete:
                    // OData spec: A successful DELETE request to the edit URL for a structural property, ... sets the property to null.
                    // The request body is ignored and should be empty.
                    // DELETE request to a non-nullable value MUST fail and the service respond with 400 Bad Request or other appropriate error.
                    if (tempSegment.Property.Type.IsNullable)
                    {
                        prefix  = "DeleteTo";
                        segment = tempSegment;
                    }
                    break;
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/key/property/cast" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/cast" ||
                     odataPath.PathTemplate == "~/singleton/property/cast" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/cast")
            {
                PropertySegment tempSegment =
                    (PropertySegment)odataPath.Segments[odataPath.Segments.Count - 2];
                TypeSegment tempCast = (TypeSegment)odataPath.Segments.Last();
                switch (method)
                {
                case ODataRequestMethod.Get:
                    prefix  = "Get";
                    segment = tempSegment;
                    cast    = tempCast;
                    break;

                case ODataRequestMethod.Post:
                    //Allow post only to collection properties
                    if (tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PostTo";
                        segment = tempSegment;
                        cast    = tempCast;
                    }
                    break;

                case ODataRequestMethod.Put:
                    prefix  = "PutTo";
                    segment = tempSegment;
                    cast    = tempCast;
                    break;

                case ODataRequestMethod.Patch:
                    // PATCH is not supported for collection properties.
                    if (!tempSegment.Property.Type.IsCollection())
                    {
                        prefix  = "PatchTo";
                        segment = tempSegment;
                        cast    = tempCast;
                    }
                    break;
                }
            }
            else if (odataPath.PathTemplate == "~/entityset/key/property/$value" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/$value" ||
                     odataPath.PathTemplate == "~/singleton/property/$value" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/$value" ||
                     odataPath.PathTemplate == "~/entityset/key/property/$count" ||
                     odataPath.PathTemplate == "~/entityset/key/cast/property/$count" ||
                     odataPath.PathTemplate == "~/singleton/property/$count" ||
                     odataPath.PathTemplate == "~/singleton/cast/property/$count")
            {
                PropertySegment tempSegment = (PropertySegment)odataPath.Segments[odataPath.Segments.Count - 2];
                switch (method)
                {
                case ODataRequestMethod.Get:
                    prefix  = "Get";
                    segment = tempSegment;
                    break;
                }
            }

            return(segment == null ? null : segment.Property);
        }
 /// <summary>
 /// Determine if the Http method is a match.
 /// </summary>
 public bool IsHttpMethodSupported(ODataRequestMethod method)
 {
     return(this.supportedHttpMethods.Contains(method));
 }
        /// <inheritdoc/>
        internal static string SelectActionImpl(ODataPath odataPath, IWebApiControllerContext controllerContext, IWebApiActionMap actionMap)
        {
            ODataRequestMethod method           = controllerContext.Request.GetRequestMethodOrPreflightMethod();
            string             actionNamePrefix = GetActionMethodPrefix(method);

            if (actionNamePrefix == null)
            {
                return(null);
            }

            if (odataPath.PathTemplate == "~/entityset/key/navigation" ||
                odataPath.PathTemplate == "~/entityset/key/navigation/$count" ||
                odataPath.PathTemplate == "~/entityset/key/cast/navigation" ||
                odataPath.PathTemplate == "~/entityset/key/cast/navigation/$count" ||
                odataPath.PathTemplate == "~/singleton/navigation" ||
                odataPath.PathTemplate == "~/singleton/navigation/$count" ||
                odataPath.PathTemplate == "~/singleton/cast/navigation" ||
                odataPath.PathTemplate == "~/singleton/cast/navigation/$count")
            {
                NavigationPropertySegment navigationSegment =
                    (odataPath.Segments.Last() as NavigationPropertySegment) ??
                    odataPath.Segments[odataPath.Segments.Count - 2] as NavigationPropertySegment;
                IEdmNavigationProperty navigationProperty = navigationSegment.NavigationProperty;
                IEdmEntityType         declaringType      = navigationProperty.DeclaringType as IEdmEntityType;

                // It is not valid to *Post* to any non-collection valued navigation property.
                if (navigationProperty.TargetMultiplicity() != EdmMultiplicity.Many &&
                    ODataRequestMethod.Post == method)
                {
                    return(null);
                }

                // It is not valid to *Put/Patch" to any collection-valued navigation property.
                if (navigationProperty.TargetMultiplicity() == EdmMultiplicity.Many &&
                    (ODataRequestMethod.Put == method || ODataRequestMethod.Patch == method))
                {
                    return(null);
                }

                // *Get* is the only supported method for $count request.
                if (odataPath.Segments.Last() is CountSegment && ODataRequestMethod.Get != method)
                {
                    return(null);
                }

                if (declaringType != null)
                {
                    // e.g. Try GetNavigationPropertyFromDeclaringType first, then fallback on GetNavigationProperty action name
                    string actionName = actionMap.FindMatchingAction(
                        actionNamePrefix + navigationProperty.Name + "From" + declaringType.Name,
                        actionNamePrefix + navigationProperty.Name);

                    if (actionName != null)
                    {
                        if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                        {
                            KeySegment keyValueSegment = (KeySegment)odataPath.Segments[1];
                            controllerContext.AddKeyValueToRouteData(keyValueSegment);
                        }

                        return(actionName);
                    }
                }
            }

            return(null);
        }