抽象通用ODataController类导致'找不到HTTP资源'

时间:2014-01-13 22:21:56

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

我正在尝试在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的Register函数仅包含以下内容:

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没有中间父类吗?

2 个答案:

答案 0 :(得分:0)

在我们的一个项目中,我们还使用通用的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

您可以通过提供Routing and Action Selection in ASP.NET Web API中所述的自定义IHttpActionSelector来实际验证和诊断解析过程(看看ASP.NET WEB API 2: HTTP Message Lifecycle也可能是值得的。)

实际上, 可能会使用GetEntity作为您最初在示例中尝试的方法名称,而无需将其重命名为简单Get。此外,您的ODATA配置无需任何修改。

答案 1 :(得分:-1)

要确定要调用的操作,框架使用路由表。 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

OR

http://localhost:42201/api/Extract/GetMessage/?quotename=Q3