.NET Core OData:重写实体集路由约定实现导航键路由
在非Core的Microsoft.AspNet.OData环境中,我们通过重写EntitySetRoutingConvention的SelectAction方法实现~/entityset/key/navigation/key格式的路由,但在.NET 6-8的Core环境下,Microsoft.AspNetCore.OData的路由约定实现逻辑有所不同,以下是具体的实现方案:
1. 自定义路由约定类
创建继承自NavigationRoutingConvention的自定义约定,匹配目标路径模板并提取路由参数:
using Microsoft.AspNetCore.OData.Routing.Conventions; using Microsoft.OData.UriParser; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; public class NavigationKeyRoutingConvention : NavigationRoutingConvention { public override string? SelectAction(ODataPath odataPath, HttpContext httpContext, ILookup<string, ActionDescriptor> actionMap) { // 匹配GET请求和指定路径模板 if (httpContext.Request.Method == HttpMethod.Get.Method && odataPath.PathTemplate == "~/entityset/key/navigation/key") { var navigationSegment = odataPath.Segments[2] as NavigationPropertySegment; if (navigationSegment == null) { return null; } // 构造目标Action名称 string actionName = $"Get{navigationSegment.NavigationProperty.Name}"; if (actionMap.Contains(actionName)) { // 提取主实体键并注入路由数据 var keySegment = odataPath.Segments[1] as KeySegment; if (keySegment != null) { httpContext.ODataFeature().RouteData.Add(ODataRouteConstants.Key, keySegment.Keys.FirstOrDefault().Value); } // 提取关联实体键并注入路由数据 var relatedKeySegment = odataPath.Segments[3] as KeySegment; if (relatedKeySegment != null) { httpContext.ODataFeature().RouteData.Add(ODataRouteConstants.RelatedKey, relatedKeySegment.Keys.FirstOrDefault().Value); } return actionName; } } // 不匹配时交给基类处理 return base.SelectAction(odataPath, httpContext, actionMap); } }
2. 注册自定义路由约定
在.NET 6+的Program.cs中,配置OData服务时注入自定义路由约定:
var builder = WebApplication.CreateBuilder(args); // 添加OData控制器服务 builder.Services.AddControllers().AddOData(options => { // 创建默认路由约定集合 var conventions = ODataRoutingConventions.CreateDefault(); // 将自定义约定插入集合头部,确保优先匹配 conventions.Insert(0, new NavigationKeyRoutingConvention()); // 配置OData路由组件 options.AddRouteComponents("odata", GetEDMModel(), services => { services.AddSingleton<IODataRoutingConvention>(new DefaultODataRoutingConvention()); services.AddRange(conventions); }); // 启用所需的OData查询选项(按需配置) options.Select().Filter().OrderBy().Expand().Count().SetMaxTop(null); }); var app = builder.Build(); // 配置中间件管道 app.UseRouting(); app.UseAuthorization(); app.MapControllers(); app.Run(); // 构建EDM模型的示例方法 private static IEdmModel GetEDMModel() { var modelBuilder = new ODataConventionModelBuilder(); // 注册实体集和导航属性,示例: modelBuilder.EntitySet<Customer>("Customers"); modelBuilder.EntitySet<Order>("Orders"); // 假设Customer包含单值导航属性Order return modelBuilder.GetEdmModel(); }
3. 编写对应的控制器Action
在实体控制器中添加匹配名称的Action,接收路由参数:
public class CustomersController : ODataController { // 匹配路径:~/odata/Customers({key})/Order({relatedKey}) public IActionResult GetOrder(int key, int relatedKey) { // 实现业务逻辑:根据key(Customer主键)和relatedKey(Order主键)查询数据 var targetOrder = /* 数据查询逻辑 */; if (targetOrder == null) { return NotFound(); } return Ok(targetOrder); } }
关键差异说明
- Core环境中,路由约定的基类改为
NavigationRoutingConvention,SelectAction方法的上下文参数从HttpControllerContext变为HttpContext,需通过httpContext.ODataFeature().RouteData操作路由数据。 - 注册方式从
MapODataServiceRoute改为在AddOData的AddRouteComponents中配置约定集合。 - 整体匹配逻辑与非Core版本一致,但API细节适配了ASP.NET Core的架构。
内容的提问来源于stack exchange,提问作者Oleg Shirokikh
相关产品推荐
相关产品推荐

