【发布时间】: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