【问题标题】:Abstract Generic ODataController Class Leads To 'No HTTP resource was found'抽象的通用 ODataController 类导致“未找到 HTTP 资源”
【发布时间】: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 引起的。基本方法必须是公开的。

标签: asp.net asp.net-web-api odata


【解决方案1】:

在我们的一个项目中,我们还使用了一个通用的 ODataController 基类,我们实际上使用 GetEntity 检索单个实体,使用 GetEntitySet 检索实体列表。

根据您提供的 URL 和生成的错误消息,ODATA 框架找不到 ~/entitysetODataAction。正如您以http://localhost:10000/odata/MyObjects 为例,相关操作不能是public SingleResult&lt;T&gt; 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 2017-12-23
      • 1970-01-01
      • 2016-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多