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

时间:2018-07-12 20:32:39

标签: c# .net asp.net-web-api odata asp.net-web-api-routing

背景/背景:

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

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

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

我们想要的:

我们只需要为路由2:/api/partial允许一部分API功能。当某人尝试访问“部分” EdmModel不可用的API时,我们想返回404

示例:

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

我们尝试的方法:

使用第二个EdmModel,该模型仅包含完整EdmModel中可用实体的子集。 (请参见第二个代码段,名为“我们想要做什么:”。)

问题:

服务启动时出现错误: 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”的路径,在我看来,这很像黑客。

我们目前正在做什么:

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);
}

我们要做什么:

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);
}

1 个答案:

答案 0 :(得分:0)

您需要两个不同的模型或更多的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);

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