仅为路由公开.NET OData API的子集(对于排除的API,返回404)

背景/环境:

我们有两条路线,路线前缀不同:

  1. 路由1前缀: /api
  2. 路由2前缀: /api/partial

目前,我们对两个路由前缀使用相同的EdmModel。 (参见第一个代码snippit,名为“我们目前正在做什么”)。

我们想要什么:

我们只需要为Route 2允许APIfunction的子集: /api/partial 。 当有人试图访问“部分”EdmModel不可用的API时,我们希望返回404

例:

  1. 我们想为/api/parial/products返回404 ,其中products未在此“部分”API路径中定义。
  2. 我们仍希望将/api/products路由到控制器方法

我们尝试过的:

使用第二个EdmModel,它只包含完整EdmModel中可用实体的子集。 (参见第二个代码snippit,名为“我们想做什么:”。)

问题:

我们在服务启动时收到错误: The path template 'products' on the action 'Export' in controller 'Products' is not a valid OData path template. Resource not found for the segment 'products'.) The path template 'products' on the action 'Export' in controller 'Products' is not a valid OData path template. Resource not found for the segment 'products'.)

我对正在发生的事情的最好猜测是.NET OData库扫描所有OData控制器,函数和操作,并期望每个路由都在EdmModel中明确定义它们。 如果这是真的,那么这个解决方案(初始化一个新的EdmModel)可能不会起作用……

这不受支持吗? 如果没有,还有什么其他选择来实现这一目标? 我们必须在控制器API函数中显式返回404吗? 这需要分析API函数中“api / subset”的路径,这在我看来就像是一个hack。

我们目前的工作:

 private static IEdmModel GetFullEdmModel() { var builder = new ODataConventionModelBuilder(); var orders = builder.EntitySet("orders"); orders.EntityType.HasKey(o => o.Id); orders.EntityType.Property(o => o.Id).Name = "id"; var products = builder.EntitySet("products"); products.EntityType.HasKey(p => p.Id); products.EntityType.Property(p => p.Id).Name = "id"; products.EntityType.Action("Export").Returns(); return builder.GetEdmModel(); } protected override void Register(HttpConfiguration config) { base.Register(config); var model = GetFullEdmModel(); var conventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting(config, model); // Map route 1 to {model} config.MapODataServiceRoute( routeName: "route1", routePrefix: "/api", model: model, pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); // Map route 2 to {model} config.MapODataServiceRoute( routeName: "route2", routePrefix: "/api/partial", // different route prefix model: model, // but it uses the same model pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); } 

我们想做什么:

 private static IEdmModel GetPartialEdmModel() { var builder = new ODataConventionModelBuilder(); // Include only one entity var orders = builder.EntitySet("orders"); orders.EntityType.HasKey(o => o.Id); orders.EntityType.Property(o => o.Id).Name = "id"; return builder.GetEdmModel(); } protected override void Register(HttpConfiguration config) { base.Register(config); // Map route 1 to {model} var model = GetFullEdmModel(); var modelConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting(config, model); config.MapODataServiceRoute( routeName: "route1", routePrefix: "/api", model: model, // use standard full model pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); // Map route 2 to a new partial model: {partialModel} var partialModel = GetPartialEdmModel(); var partialModelConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting(config, model); config.MapODataServiceRoute( routeName: "route2", routePrefix: "/api/partial", // different route prefix model: partialModel, // use a sparate, partial edm model ( a subset of the full edm model ) pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); } 

您需要两种不同的型号或更具intelligent型号,因为有权使用的products并非在所有路径中都可用,而只能在/api/products
一般来说,错误消息已经很好地解释了它,但也许你只需要它就可以了。

我认为更清洁的方法是为每条路线提供一个自己的模型,然后很容易添加或删除你需要的内容。
如果在一个模型中混合所有内容,如果添加或更改新路径,它将始终在构建中。

 // Map route 1 to {model} config.MapODataServiceRoute( routeName: "route1", routePrefix: "/api", model: GetApiModel(), pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); // Map route 2 to {model} config.MapODataServiceRoute( routeName: "route2", routePrefix: "/api/partial", // different route prefix model: GetApiPartialModel(), pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); 

这是一个概念,我对代码中的符号并不那么坚定,所以你可能需要稍微调整一下。

或者,如果您真的只想使用一个模型,请尝试这样:

 // Map route 1 to {model} config.MapODataServiceRoute( routeName: "route1", routePrefix: "/api", model: GetFullEdmModel("/api"), pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); // Map route 2 to {model} config.MapODataServiceRoute( routeName: "route2", routePrefix: "/api/partial", // different route prefix model: GetFullEdmModel("/api/partial"), // but it uses the same model pathHandler: new CustomBIODataPathHandler(), routingConventions: conventions); 

然后,您可以使用该参数来实现任何条件或开关。

除此之外,您可能在底部的所需代码中出现错误:

 // Map route 1 to {model} var model = GetFullEdmModel(); var modelConventions = ODataRoutingConventions.CreateDefaultWithAttributeRouting(config, model); config.MapODataServiceRoute( routeName: "route1", routePrefix: "/api", model: model, // use standard full model pathHandler: new CustomBIODataPathHandler(), routingConventions: modelConventions); 

查看最后一行,然后必须针对这两个块进行调整。 关于每个配置块上面的两行,我从来没有注意过,所以主要的问题可能是并非所有变量都适合在一起,你需要在所有细节中检查它。