public void Ctor_SetRoutePrefixCorrectly(string prefix) { // Assert ODataRouteAttribute routeTemplate = new ODataRouteAttribute("({key})", prefix); // Act & Assert Assert.Equal("({key})", routeTemplate.PathTemplate); Assert.Equal(prefix, routeTemplate.RoutePrefix); }
/// <summary> /// /// </summary> /// <param name="context"></param> public virtual bool AppliesToAction(ODataControllerActionContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } ActionModel action = context.Action; ODataRouteAttribute routeAttr = action.GetAttribute <ODataRouteAttribute>(); if (routeAttr == null) { return(false); } string prefix = context.Prefix; IEdmModel model = context.Model; string routeTemplate = ""; ODataRoutePrefixAttribute prefixAttr = action.Controller.GetAttribute <ODataRoutePrefixAttribute>(); if (prefixAttr != null) { routeTemplate = prefixAttr.Prefix + "/"; } routeTemplate += routeAttr.PathTemplate; SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null); if (selectorModel == null) { selectorModel = new SelectorModel(); action.Selectors.Add(selectorModel); } string templateStr = string.IsNullOrEmpty(prefix) ? routeTemplate : $"{prefix}/{routeTemplate}"; selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr) { Name = templateStr }); selectorModel.EndpointMetadata.Add(new ODataEndpointMetadata(prefix, model, templateStr)); return(true); }
internal ODataRouteBuilderContext( HttpConfiguration configuration, ODataRoute route, HttpActionDescriptor actionDescriptor, IReadOnlyList <ApiParameterDescription> parameterDescriptions, ModelTypeBuilder modelTypeBuilder, ODataApiExplorerOptions options) { Contract.Requires(configuration != null); Contract.Requires(route != null); Contract.Requires(actionDescriptor != null); Contract.Requires(parameterDescriptions != null); Contract.Requires(modelTypeBuilder != null); Contract.Requires(options != null); serviceProvider = configuration.GetODataRootContainer(route); EdmModel = serviceProvider.GetRequiredService <IEdmModel>(); AssembliesResolver = configuration.Services.GetAssembliesResolver(); routeAttribute = actionDescriptor.GetCustomAttributes <ODataRouteAttribute>().FirstOrDefault(); RouteTemplate = routeAttribute?.PathTemplate; Route = route; ActionDescriptor = actionDescriptor; ParameterDescriptions = parameterDescriptions; Options = options; UrlKeyDelimiter = configuration.GetUrlKeyDelimiter(); var container = EdmModel.EntityContainer; if (container == null) { IsRouteExcluded = true; return; } EntitySet = container.FindEntitySet(actionDescriptor.ControllerDescriptor.ControllerName); Operation = container.FindOperationImports(actionDescriptor.ActionName).FirstOrDefault()?.Operation ?? EdmModel.FindDeclaredOperations(container.Namespace + "." + actionDescriptor.ActionName).FirstOrDefault(); ActionType = GetActionType(EntitySet, Operation); if (Operation?.IsAction() == true) { ConvertODataActionParametersToTypedModel(modelTypeBuilder, (IEdmAction)Operation); } }
public IEnumerable <ControllerActionDescriptor> SelectAction(RouteContext routeContext) { // Get a IActionDescriptorCollectionProvider from the global service provider IActionDescriptorCollectionProvider actionCollectionProvider = routeContext.HttpContext.RequestServices.GetRequiredService <IActionDescriptorCollectionProvider>(); Contract.Assert(actionCollectionProvider != null); // Get OData path from HttpContext Microsoft.AspNet.OData.Routing.ODataPath odataPath = routeContext.HttpContext.ODataFeature().Path; HttpRequest request = routeContext.HttpContext.Request; if (request.Method == "GET" && odataPath.PathTemplate.Equals("~/entityset/key")) { string controllerName = odataPath.Segments[0].Identifier; // Get the list of action descriptors for this controller var actionDescriptors = actionCollectionProvider.ActionDescriptors.Items .Cast <ControllerActionDescriptor>() .Where(ad => ad.ControllerName == controllerName && ad.ActionName == "Get"); foreach (var actionDescriptor in actionDescriptors.OrderByDescending(item => item.ActionName.Length)) { // We are only interested in examining actions with two parameters. if (actionDescriptor.Parameters.Count != 1) { continue; } // See if an OdataRouteAttribute exists in the EndpointMetaData ODataRouteAttribute oDataRouteAttribute = actionDescriptor.EndpointMetadata.OfType <ODataRouteAttribute>().FirstOrDefault(); string actionName = actionDescriptor.ActionName; Microsoft.OData.UriParser.KeySegment keyFirst = odataPath.Segments[1] as Microsoft.OData.UriParser.KeySegment; object keyParentId = keyFirst.Keys.ToList()[0].Value; routeContext.RouteData.Values["odataPath"] = odataPath; routeContext.RouteData.Values[actionDescriptor.Parameters[0].Name] = keyParentId; routeContext.RouteData.Values[ODataRouteConstants.Key] = keyParentId; return(new[] { actionDescriptor }); } } else if (request.Method == "GET" && odataPath.PathTemplate.Equals("~/entityset/key/unresolved")) { string controllerName = odataPath.Segments[0].Identifier; // Get the list of action descriptors for this controller var actionDescriptors = actionCollectionProvider.ActionDescriptors.Items .Cast <ControllerActionDescriptor>() .Where(ad => ad.ControllerName == controllerName); Microsoft.OData.UriParser.KeySegment keyFirst = odataPath.Segments[1] as Microsoft.OData.UriParser.KeySegment; object keyParentId = keyFirst.Keys.ToList()[0].Value; string unresolvedPattern = odataPath.Segments[2].ToString(); // Go from longest named action to shortest to ensure that we don't confuse which action to // call if two actions have similar names such that one is a sub-set of the other. foreach (var actionDescriptor in actionDescriptors.OrderByDescending(item => item.ActionName.Length)) { // We are only interested in examining actions with two parameters. if (actionDescriptor.Parameters.Count != 2) { continue; } // See if an OdataRouteAttribute exists in the EndpointMetaData ODataRouteAttribute oDataRouteAttribute = actionDescriptor.EndpointMetadata.OfType <ODataRouteAttribute>().FirstOrDefault(); string actionName = actionDescriptor.ActionName; if (oDataRouteAttribute != null) { string pathTemplate = oDataRouteAttribute.PathTemplate; // The only template we want to match is in the shape "{ControllerName}({Parameter 1 Name})/{FunctionName}({Parameter 2 Name}) // If the pattern doesn't match that, then return null; string compareStartPath = string.Format("{0}({{{1}}})/", controllerName, actionDescriptor.Parameters[0].Name); string compareEndPath = string.Format("({{{0}}})", actionDescriptor.Parameters[1].Name); if (!pathTemplate.StartsWith(compareStartPath) || !pathTemplate.EndsWith(compareEndPath)) { return(null); } actionName = pathTemplate.Substring(compareStartPath.Length, pathTemplate.Length - compareStartPath.Length - compareEndPath.Length); } // The basic assumption here is that the unresolved segment is a string that looks like this: // "SomeFunctionName(someParameterValue)" // So the code below attempts to match the right action and strip out the parameter value // with simple string manipulations. unresolvedPattern = unresolvedPattern.Replace(actionName, string.Empty); if (unresolvedPattern.StartsWith("(")) { unresolvedPattern = unresolvedPattern.Substring(1); } else { continue; } if (unresolvedPattern.EndsWith(")")) { unresolvedPattern = unresolvedPattern.Substring(0, unresolvedPattern.Length - 1); } else { continue; } // What is left in unresolvedPattern should be a single value representing the // second key value for the function. Use what we know to set the route data values // and return this matching action descriptor. routeContext.RouteData.Values["odataPath"] = odataPath; routeContext.RouteData.Values[actionDescriptor.Parameters[0].Name] = keyParentId; routeContext.RouteData.Values[actionDescriptor.Parameters[1].Name] = unresolvedPattern; routeContext.RouteData.Values[ODataRouteConstants.Key] = keyParentId; routeContext.RouteData.Values[ODataRouteConstants.RelatedKey] = unresolvedPattern; return(new[] { actionDescriptor }); } } //else if ("~/entityset/key/navigation") return(null); }