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

Jam*_*zba 9 .net c# odata asp.net-web-api asp.net-web-api-routing

背景/环境:

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

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

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

我们想要什么:

我们只需要为路由2允许API功能的子集:/api/partial.404当有人试图访问"部分"EdmModel不可用的API时,我们希望返回

例:

  1. 我们希望返回404/api/parial/products,其中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'.)

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

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

我们目前的工作:

private static IEdmModel GetFullEdmModel()
{
    var builder = new ODataConventionModelBuilder();

    var orders = builder.EntitySet<Order>("orders");
    orders.EntityType.HasKey(o => o.Id);
    orders.EntityType.Property(o => o.Id).Name = "id";

    var products = builder.EntitySet<Product>("products");
    products.EntityType.HasKey(p => p.Id);
    products.EntityType.Property(p => p.Id).Name = "id";
    products.EntityType.Action("Export").Returns<ExportResponse>();

    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);
}
Run Code Online (Sandbox Code Playgroud)

我们想做什么:

private static IEdmModel GetPartialEdmModel()
{
    var builder = new ODataConventionModelBuilder();

    // Include only one entity
    var orders = builder.EntitySet<Order>("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);
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*vid 1

您需要两个不同的模型或更多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);
Run Code Online (Sandbox Code Playgroud)

这是一个概念,我对该代码中的符号不​​太确定,因此您可能需要对其进行一些调整。

或者,如果您确实只想使用一种模型,请像这样尝试:

// 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);
Run Code Online (Sandbox Code Playgroud)

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

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

// 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);
Run Code Online (Sandbox Code Playgroud)

请参阅最后一行,然后必须对两个块进行调整。我以前从未关心过每个配置块上方的两行,因此主要问题可能是并非所有变量都适合在一起,您必须检查所有细节。