ASP.NET WebApi2 OData 处理带有斜杠的查询 /
ASP.NET WebApi2 OData handling of queries with slash /
我用约定模型路由制作了一个 "standard" Web Api 2 OData 项目。以下 OData 查询正在运行:
/odata/Users
/odata/Users(123)
/odata/$metadata
/odata/Users?$select=Username
所以在我尝试这个之前一切似乎都很好,我认为这也是一个合法的 OData 查询:
/odata/Users(123)/Username
查询中的斜线 / 破坏了一切,它根本没有命中控制器 class 和 OData 身份验证流程。 Microsoft ASP.NET OData 实施是否应完全支持这一点?还是仅当我为每个 属性 之类的用户名定义具有正确路由的显式方法时才支持?有什么建议可以解决这个问题吗?我已经尝试过明确的 {*rest} 路线等
AFAIK,内置路由约定不包括用于 属性 访问的约定。您需要为每次 属性 访问添加许多操作。
但是,基于此资源 here,添加自定义路由约定来处理 属性 访问路径模板并不难:~/entityset/key/property
这是根据我上面分享的 link 改编的自定义路由约定
使用的程序集:Microsoft.AspNet.OData 7.4.1 - 该方法对于您可能正在使用的任何其他 OData Web API 库都是相同的
Class用于说明
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
我用约定模型路由制作了一个 "standard" Web Api 2 OData 项目。以下 OData 查询正在运行:
/odata/Users
/odata/Users(123)
/odata/$metadata
/odata/Users?$select=Username
所以在我尝试这个之前一切似乎都很好,我认为这也是一个合法的 OData 查询:
/odata/Users(123)/Username
查询中的斜线 / 破坏了一切,它根本没有命中控制器 class 和 OData 身份验证流程。 Microsoft ASP.NET OData 实施是否应完全支持这一点?还是仅当我为每个 属性 之类的用户名定义具有正确路由的显式方法时才支持?有什么建议可以解决这个问题吗?我已经尝试过明确的 {*rest} 路线等
AFAIK,内置路由约定不包括用于 属性 访问的约定。您需要为每次 属性 访问添加许多操作。
但是,基于此资源 here,添加自定义路由约定来处理 属性 访问路径模板并不难:~/entityset/key/property
这是根据我上面分享的 link 改编的自定义路由约定
使用的程序集:Microsoft.AspNet.OData 7.4.1 - 该方法对于您可能正在使用的任何其他 OData Web API 库都是相同的
Class用于说明
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