仅为路由公开.NET OData API的子集(排除的API返回404) [英] Expose only a subset of .NET OData APIs for a route (return 404 for excluded APIs)

查看:46
本文介绍了仅为路由公开.NET OData API的子集(排除的API返回404)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景/背景:

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

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

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

我们想要的东西:

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

示例:

  1. 我们要为/api/parial/products返回404,其中在此部分" 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);
}

解决方案

您需要两个不同的模型或一个具有条件的更多intelligent模型,因为实体products并非在所有路径中都可用,而仅在/api/products中可用. br> 通常,错误消息已经很好地解释了它,但是也许您只需要换句话说即可.

我认为更简洁的方法是为每条路线提供一个自己的模型,然后在内部添加或删除任何所需的东西都很容易.
如果您将所有内容混合在一个模型中,那么如果添加或更改新路线,它将始终处于构建状态.

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

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

Background/Context:

We have two routes, with different route prefixes:

  1. Route 1 prefix: /api
  2. Route 2 prefix: /api/partial

Currently, we use the same EdmModel for both route prefixes. (See the first code snippit, named "What we currently do").

What we want:

We need to only allow a subset of API functionality for Route 2: /api/partial. We want to return 404 when someone tries to access an API that is not available to the "partial" EdmModel

Example:

  1. We want to return 404 for /api/parial/products, where products is not defined in this "partial" API route.
  2. We want to still route /api/products to the controller method

What we've tried:

Using a second EdmModel, that contains only a subset of the entities available in the full EdmModel. (See the second code snippit, named "What we want to do:".)

Problem:

We get an error on service startup: 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'.)

My best guess at what is happening is that the .NET OData library scans all of the OData controllers, functions, actions and expect each of them to be explicitly defined in the EdmModel for each route. If this is true, then this solution (initializing a new EdmModel) will likely not work...

Is this not supported? If not, what other options are there to accomplish this? Must we explicitly return 404 in the controller API function? This would require analyzing the path for "api/subset" in the API function, which seems to me like a hack.

What we currently do:

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

What we want to do:

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

解决方案

You need two different models or a more intelligent model with conditions as entitiy products is not available in all paths but only in /api/products.
In general the Error-message is explaining it already quite well but perhaps you just need it in other words.

I think the cleaner way is to provide an own model for each route, then it's quite easy to add or remove whatever you need inside.
If you mix everything in one model it will always be under construction if a new route is added or changed.

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

It's a concept and I'm not so firm with the notation in that code so you might have to adjust it a bit.

Alternatively, if you really want to use only one model just try it like this:

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

Then you can use the parameter to implement any conditions or a switch.

Beside that you've probably faults in your desired code in the bottom:

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

See the last line, that has to be adjusted for both blocks then. I never took care before about the two lines above each config-block, so primary the problem is probably that not all variables are fitting together and you've to check it in all details.

这篇关于仅为路由公开.NET OData API的子集(排除的API返回404)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆