【问题标题】:ASP.NET WebApi2 OData handling of queries with slash /ASP.NET WebApi2 OData 处理带有斜线的查询 /
【发布时间】:2020-09-15 17:40:54
【问题描述】:

我制作了一个带有约定模型路由的“标准”Web Api 2 OData 项目。以下 OData 查询正在运行:

/odata/Users

/odata/Users(123)

/odata/$metadata

/odata/Users?$select=Username

所以在我尝试这个之前一切似乎都很好,我认为这也是一个合法的 OData 查询:

/odata/Users(123)/Username

查询中的斜杠 / 会破坏一切,它根本不会影响控制器类和 OData 身份验证流程。 Microsoft ASP.NET OData 实现中是否应该完全支持这一点?还是仅当我为每个属性(如用户名)定义具有正确路由的显式方法时才支持此功能?有什么建议可以解决这个问题吗?我已经尝试过明确的 {*rest} 路线等。

【问题讨论】:

    标签: asp.net-web-api asp.net-web-api2 odata asp.net-web-api-routing


    【解决方案1】:

    AFAIK,内置路由约定不包括用于属性访问的约定。您需要为每个属性访问添加许多操作。

    但是,基于此资源here,添加自定义路由约定来处理属性访问路径模板并不是那么困难:~/entityset/key/property

    这是一个自定义路由约定,改编自我上面分享的链接

    使用的程序集:Microsoft.AspNet.OData 7.4.1 - 对于您可能使用的任何其他 OData Web API 库,该方法都是相同的

    用于说明的类

    public class Product
    {
        [Key]
        public int Id { get; set; }
        public string Name { get; set; }
    }
    

    为属性访问添加路由约定

    // Usings
    using Microsoft.AspNet.OData.Routing;
    using Microsoft.AspNet.OData.Routing.Conventions;
    using System;
    using System.Linq;
    using System.Web.Http.Controllers;
    // ...
    
    public class CustomPropertyRoutingConvention : NavigationSourceRoutingConvention
    {
        private const string ActionName = "GetProperty";
    
        public override string SelectAction(ODataPath odataPath, HttpControllerContext controllerContext, ILookup<string, HttpActionDescriptor> actionMap)
        {
            if (odataPath == null || controllerContext == null || actionMap == null)
            {
                return null;
            }
    
            if (odataPath.PathTemplate == "~/entityset/key/property" ||
                odataPath.PathTemplate == "~/entityset/key/cast/property" ||
                odataPath.PathTemplate == "~/singleton/property" ||
                odataPath.PathTemplate == "~/singleton/cast/property")
            {
                var segment = odataPath.Segments.OfType<Microsoft.OData.UriParser.PropertySegment>().LastOrDefault();
    
                if (segment != null)
                {
                    string actionName = FindMatchingAction(actionMap, ActionName);
    
                    if (actionName != null)
                    {
                        if (odataPath.PathTemplate.StartsWith("~/entityset/key", StringComparison.Ordinal))
                        {
                            var keySegment = odataPath.Segments.OfType<Microsoft.OData.UriParser.KeySegment>().FirstOrDefault();
                            if (keySegment == null || !keySegment.Keys.Any())
                                throw new InvalidOperationException("This link does not contain a key.");
    
                            controllerContext.RouteData.Values[ODataRouteConstants.Key] = keySegment.Keys.First().Value;
                        }
    
                        controllerContext.RouteData.Values["propertyName"] = segment.Property.Name;
    
                        return actionName;
                    }
                }
            }
    
            return null;
        }
    
        public static string FindMatchingAction(ILookup<string, HttpActionDescriptor> actionMap, params string[] targetActionNames)
        {
            foreach (string targetActionName in targetActionNames)
            {
                if (actionMap.Contains(targetActionName))
                {
                    return targetActionName;
                }
            }
    
            return null;
        }
    }
    

    在控制器中添加单个方法来处理任何属性的请求

    public class ProductsController : ODataController
    {
        // ...
        [HttpGet]
        public IHttpActionResult GetProperty(int key, string propertyName)
        {
            var product = _db.Products.FirstOrDefault(d => d.Id.Equals(key));
            if (product == null)
            {
                return NotFound();
            }
    
            PropertyInfo info = typeof(Product).GetProperty(propertyName);
    
            object value = info.GetValue(product);
    
            return Ok(value, value.GetType());
        }
    
        private IHttpActionResult Ok(object content, Type type)
        {
            var resultType = typeof(OkNegotiatedContentResult<>).MakeGenericType(type);
            return Activator.CreateInstance(resultType, content, this) as IHttpActionResult;
        }
        // ...
    }
    

    在您的 WebApiConfig.cs(或您配置服务的等效位置)中

    var modelBuilder = new ODataConventionModelBuilder();
    modelBuilder.EntitySet<Product>("Products");
    
    var routingConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting("odata", configuration);
    routingConventions.Insert(0, new CustomPropertyRoutingConvention());
    
    configuration.MapODataServiceRoute("odata", "odata", modelBuilder.GetEdmModel(), new DefaultODataPathHandler(), routingConventions);
    configuration.Count().Filter().OrderBy().Expand().Select().MaxTop(null);
    configuration.EnsureInitialized();
    

    请求名称属性:/Products(1)/Name

    Id 属性请求:/Products(1)/Id

    【讨论】:

      猜你喜欢
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      • 2017-06-03
      • 2018-12-31
      • 1970-01-01
      • 2013-10-23
      • 1970-01-01
      • 2012-06-02
      相关资源
      最近更新 更多