Abstract Generic ODataController Class Leads To 'No HTTP resource was found' Abstract Generic ODataController Class Leads To 'No HTTP resource was found' asp.net asp.net

Abstract Generic ODataController Class Leads To 'No HTTP resource was found'


In one of our projects We also use a generic ODataController base class where we actually use GetEntity for retrieving single entities and GetEntitySet for retrieving a list of entities.

According to your supplied URL and the resulting error message, the ODATA framework cannot find an ODataAction for ~/entityset. As you have given http://localhost:10000/odata/MyObjects as the example, the action in question cannot be public SingleResult<T> GetEntity([FromODataUri] int key) as this only corresponds to a query like this http://localhost:10000/odata/MyObjects(42).

Our code for a generic controller looks like this:

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));    }}

The code for a specific controller then is as short as this:

public partial class KeyNameValuesController : OdataControllerBase<T>{    public KeyNameValuesController(/* ... */)        : base()    {        // there is nothing to be done here    }}

However we found out that both Get methods (for single result and enumerable result) actually have to start with Get. First we tried List instead of GetEntitySet and this did not work, as the framework then expects a POST for the List action).

You can actually verify and diagnose the resolving process by supplying a custom IHttpActionSelector as described in Routing and Action Selection in ASP.NET Web API (ahving a look at ASP.NET WEB API 2: HTTP Message Lifecycle might also be worth it).

So actually it is possible to use GetEntity as your method name as you originally tried in your example and there is no need to rename it to simple Get. In addition, there is no need for any modification in your ODATA configuration.


To determine which action to invoke, the framework uses a routing table. The Visual Studio project template for Web API creates a default route:

routes.MapHttpRoute(name: "API Default",routeTemplate: "api/{controller}/{id}",defaults: new { id = RouteParameter.Optional });

Routing by Action Name

With the default routing template, Web API uses the HTTP method to select the action. However, you can also create a route where the action name is included in the URI:

routes.MapHttpRoute(name: "ActionApi",routeTemplate: "api/{controller}/{action}/{id}",defaults: new { id = RouteParameter.Optional });

I configured config as follows:

config.Routes.MapHttpRoute(            name: "GetMessage",            routeTemplate: "api/{controller}/{action}/{quoteName}",            defaults: new { quoteName = RouterParameters.Optional }        );

Access your URI like this:

http://localhost:42201/api/Extract/GetMessage/Q3

OR

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