抽象的通用 ODataController 类导致“未找到 HTTP 资源”
Posted
技术标签:
【中文标题】抽象的通用 ODataController 类导致“未找到 HTTP 资源”【英文标题】:Abstract Generic ODataController Class Leads To 'No HTTP resource was found' 【发布时间】:2014-02-01 20:44:29 【问题描述】:我正在尝试抽象 VS 2013 中自动生成的 ODataController 类,因为除了 POCO 的名称之外,不同控制器的代码看起来相同,因此,我执行了以下操作:
public abstract class ODataControllerBase<T,DB> : ODataController
where T : class, IIdentifiable, new()
where DB : DbContext, new()
protected DB _DataContext;
public ODataControllerBase() : base()
_DataContext = new DB();
// only one function shown for brevity
[Queryable]
public SingleResult<T> GetEntity([FromODataUri] int key)
return SingleResult.Create(_DataContext.Set<T>().Where(Entity => Entity.Id.Equals(key)));
IIdentifiable
是一个接口,它强制 T 参数具有可读/可写的 Id 整数属性。
实现看起来像这样(POCO 和 DataContexts 应该已经创建好了)
public class MyObjectsController : ODataControllerBase<MyObject,MyDbContext>
public MyObjectsController() : base()
// That's it - done because all the repetitive code has been abstracted.
现在,我的 WebApiConfig 的注册函数只包含以下内容:
public static void Register(HttpConfiguration config)
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<MyObject>("MyObjects");
config.Routes.MapODataRoute("odata", "odata", builder.GetEdmModel());
我运行该项目,http://localhost:10000/odata/MyObjects
并得到响应:
<m:error>
<m:code/>
<m:message xml:lang="en-US">No HTTP resource was found that
matches the request URI `http://localhost:10000/odata/MyObjects.`
</m:message>
<m:innererror>
<m:message>No routing convention was found to select an action
for the OData path with template '~/entityset'.
</m:message>
<m:type/>
<m:stacktrace/>
</m:innererror>
</m:error>
缺少什么?我应该删除什么?这是我们不能做的事情吗,即我们真的需要直接继承ODataController
而不需要中间父类吗?
【问题讨论】:
你有返回所有对象的动作吗?例如Get()
这样的动作?
是的。示例函数是一个返回一个对象的 Get 函数...除非 Get 操作也必须命名为 GetMyObject,因为操作名称是路由敏感的?如果是这样,那么这就是其中的秘密之一。
问题已修复:将操作 GetEntity([FromODataUri]int key) 更改为普通 Get([FromODataUri]int key)。抽象控制器时,不要在 CRUD 操作上附加任何内容。
@MickaelCaruso - 你想自己发布答案,这个问题从外面看起来没有答案
我遇到了同样的问题,但我的问题是由于在 BaseController 中的 Get 方法上使用 protected 而不是 public 引起的。基本方法必须是公开的。
【参考方案1】:
在我们的一个项目中,我们还使用了一个通用的 ODataController 基类,我们实际上使用 GetEntity
检索单个实体,使用 GetEntitySet
检索实体列表。
根据您提供的 URL 和生成的错误消息,ODATA 框架找不到 ~/entityset
的 ODataAction。正如您以http://localhost:10000/odata/MyObjects
为例,相关操作不能是public SingleResult<T> GetEntity([FromODataUri] int key)
,因为这仅对应于http://localhost:10000/odata/MyObjects(42)
之类的查询。
我们的通用控制器代码如下所示:
public abstract class OdataControllerBase<T> : ODataController
where T : class, IIdentifiable, new()
protected OdataControllerBase(/* ... */)
: base()
// ...
public virtual IHttpActionResult GetEntity([FromODataUri] long key, ODataQueryOptions<T> queryOptions)
// ...
return Ok(default(T));
public virtual async Task<IHttpActionResult> GetEntitySet(ODataQueryOptions<T> queryOptions)
// ...
return Ok<IEnumerable<T>>(default(List<T>));
public virtual IHttpActionResult Put([FromODataUri] long key, T modifiedEntity)
// ...
return Updated(default(T));
public virtual IHttpActionResult Post(T entityToBeCreated)
// ...
return Created(default(T));
[AcceptVerbs(HTTP_METHOD_PATCH, HTTP_METHOD_MERGE)]
public virtual IHttpActionResult Patch([FromODataUri] long key, Delta<T> delta)
// ...
return Updated(default(T));
public virtual IHttpActionResult Delete([FromODataUri] long key)
// ...
return Updated(default(T));
特定控制器的代码如下所示:
public partial class KeyNameValuesController : OdataControllerBase<T>
public KeyNameValuesController(/* ... */)
: base()
// there is nothing to be done here
但是我们发现两个 Get 方法(用于单个结果和可枚举结果)实际上都必须以 Get
开头。首先,我们尝试使用List
而不是GetEntitySet
,但这不起作用,因为框架随后期望POST
用于List
操作。
您实际上可以通过提供自定义IHttpActionSelector
来验证和诊断解决过程,如Routing and Action Selection in ASP.NET Web API 中所述(看看ASP.NET WEB API 2: HTTP Message Lifecycle 也可能值得)。
所以实际上是可以使用GetEntity
作为您最初在示例中尝试的方法名称,并且无需将其重命名为简单的Get
。此外,无需对您的 ODATA 配置进行任何修改。
【讨论】:
【参考方案2】:为了确定调用哪个操作,框架使用路由表。 Web API 的 Visual Studio 项目模板创建默认路由:
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/controller/id",
defaults: new id = RouteParameter.Optional
);
按动作名称路由
使用默认路由模板,Web API 使用 HTTP 方法来选择操作。但是,您也可以创建一个路由,其中操作名称包含在 URI 中:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/controller/action/id",
defaults: new id = RouteParameter.Optional
);
我的配置如下:
config.Routes.MapHttpRoute(
name: "GetMessage",
routeTemplate: "api/controller/action/quoteName",
defaults: new quoteName = RouterParameters.Optional
);
像这样访问你的 URI:
http://localhost:42201/api/Extract/GetMessage/Q3
或
http://localhost:42201/api/Extract/GetMessage/?quotename=Q3
【讨论】:
以上是关于抽象的通用 ODataController 类导致“未找到 HTTP 资源”的主要内容,如果未能解决你的问题,请参考以下文章
OData 8 - ODataController 上的新输入参数语法是啥?