private static bool IsSupportedRequestMethod(ODataRequestMethod method) { return(ODataRequestMethod.Delete == method || ODataRequestMethod.Put == method || ODataRequestMethod.Post == method || ODataRequestMethod.Get == method); }
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); }
/// <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); } } }
/// <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); }