【问题标题】:ASP.Core ODataControllers not recognizing "get by key" operationASP.Core ODataControllers 无法识别“按键获取”操作
【发布时间】:2019-05-27 18:21:30
【问题描述】:

我正在研究如何在 ASP.Core 中使用 OData。

我创建了一个BooksController,是ODataController 的子类,我在其中定义了两个操作:Get()Get(int id)

/odata/books 解析到第一个 Action,但 /odata/books(1) 找不到第二个 Action。

一旦模型被定义,它就能找到以下控制器:

[ODataRoutePrefix("Books")]
public class BooksController : ODataController
{
    private BookStoreContext _db;

    public BooksController(BookStoreContext context)
    {
        _db = context;
    }

    [ODataRoute]
    [EnableQuery]
    public IActionResult Get()
    {
        return Ok(_db.Books);
    }

    [EnableQuery]
    [ODataRoute("({key})")]
    public IActionResult Get([FromODataUri] int key)
    {
        return Ok(_db.Books.FirstOrDefault(c => c.Id == key.ToGuid()));
    }
}

该站点对所有路由都有默认的约定规则(见下文)。 但我认为这不起作用,因为BooksController[ODataRoutePrefix("Books")] 装饰,而[ODataRoute](和[EnableQuery])的动作——我认为,作为基于属性的路由,优先(这是一个正确的假设?)。

我的 dto 模型是使用反射注册的...),但关键部分是 Startup 调用 UseMvc(...) 并定义路由,最终在此处调用:

private void CreateODataRoutes(IRouteBuilder routeBuilder)
{
    // register the convention routes for MVC first...
            routeBuilder.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
    ...

    // then do the OData stuff...

    routeBuilder.Count().Expand().Filter()
        .MaxTop(100).OrderBy().Select();

    // Use method further down the page 
    // to create a Build Model by reflection, using
    // all OData Model definitions (ie, classes that implement
    // IAllModulesOdataModelBuilderConfiguration)
    var oDataConventionModelBuilder = BuildODataModelUsingReflectionAcrossAllAssemblies();

    // Use the modelBuilder as the basis of defining routes:
    RegisterRoutesToODataController(routeBuilder, oDataConventionModelBuilder);
}

BuildODataModelUsingReflectionAcrossAllAssemblies 使用反射来查找单独的模型定义,每一个都非常简单,只定义了它们的 id(其余的依赖于约定)。

请注意,我并没有像过去那样按照惯例定义操作(请参阅下文)。

    public class BookODataModelBuilderConfigurationBase<T> : IAllModulesOdataModelBuilderConfiguration
        where T : class, IHasGuidId, new()
    {

        public virtual void Apply(ODataModelBuilder builder ...) 
        {
            var _controllerName = this.GetControllerNameByConvention(typeof(Book));
            var entity = builder.EntitySet<T>(this._controllerName).EntityType;
            entity.HasKey(x => x.Id);
        //Note...no Actions defined, as planning to rely on default conventions (routing by Verb to method starting with Get...)
        }
    }

模型创建时,注册如下;

        private void RegisterRoutesToODataController(IRouteBuilder routeBuilder,
            ODataConventionModelBuilder oDataConventionModelBuilder)
        {
            string routePrefix = $"{App.Modules.Core.Shared.Constants.ModuleSpecific.Module.AssemblyNamePrefix}.";

            // Build the Edm model used to parse commands:
            var edmModel = oDataConventionModelBuilder.GetEdmModel();

            // Register the Odata paths
            routeBuilder.MapODataServiceRoute(
                routeName: $"{routePrefix}odataDefault",
                routePrefix: "odata",
                edmModel,
                pathHandler:new DefaultODataPathHandler(),
                // By convention? So that Get verb goes to Get action, etc.
                routingConventions: ODataRoutingConventions.CreateDefault()
            );
        }

当路径为/odata/book(1)时返回HTTP ERROR 404,页面不存在。

谢谢!

我尝试过的其他事情包括:

  • 注释掉 SwaggerAPI 的配置
  • 删除了关键参数上的[FromODataUri](有必要吗?)
  • 添加/删除[ODataRoute("({key})")]
  • 将控制器注册为BooksController 复数/单数
  • 将操作名称更改为GetBook,然后又改回Get
  • 添加/删除ODataRoutePrefix
  • 在注册默认约定路由之前注册 OData 路由(认为应该一直如此,对吧?)。
  • ...所有这些都开始看起来更像是绝望而不是编码:-( ...
  • 仍在寻找。感谢您的指导。

【问题讨论】:

  • 在控制器属性[ODataRoutePrefix("Books")] 中,您使用大写字母指定“书籍”。但是您的测试网址是/odata/books(1),带有一个小“b”。你能测试看看这是否是问题所在?
  • 感谢 GwigWag。它不区分大小写。两者都有效。

标签: asp.net-core odata asp.net-core-webapi


【解决方案1】:

天哪。 (不好意思地)解决了。 不是框架、Nuget、控制器基类、路由前缀、路由或任何光荣的东西,而是……我。 我唯一没有看的地方是模型本身,它将 Id 定义为 Guid。 Action 使用的是 int,将其转换为 Guid。 ASP.Core 找不到它,因为它正在基于模型(而不是控制器)构建路由,因此忽略了操作,因为它将基于约定的路由构建为 int != Guid 毫无意义。呵呵。

如果您想知道我为什么要使用 int...那是因为当我为 Db 播种时,我想要一个 Guid Key,但出于测试目的,我希望一些记录具有我可以参考的特定 Id ,而且我很懒,不想输入完整的 Guid。

回想起来,那是个愚蠢的想法……:-(

但是感谢您调查它!感谢您花费的时间。

【讨论】:

  • 很高兴听到您发现了问题。您可能应该将此答案标记为“已接受的答案”。
猜你喜欢
  • 1970-01-01
  • 2021-05-11
  • 2021-12-04
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多