WebAPI and ODataController return 406 Not Acceptable

39,906

Solution 1

The order in which the routes are configured has an impact. In my case, I also have some standard MVC controllers and help pages. So in Global.asax:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(config =>
    {
        ODataConfig.Register(config); //this has to be before WebApi
        WebApiConfig.Register(config); 

    });
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
}

The filter and routeTable parts weren't there when I started my project and are needed.

ODataConfig.cs:

public static void Register(HttpConfiguration config)
{
    config.MapHttpAttributeRoutes(); //This has to be called before the following OData mapping, so also before WebApi mapping

    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();

    builder.EntitySet<Site>("Sites");
    //Moar!

    config.MapODataServiceRoute("ODataRoute", "api", builder.GetEdmModel());
}

WebApiConfig.cs:

public static void Register(HttpConfiguration config)
{
    config.Routes.MapHttpRoute( //MapHTTPRoute for controllers inheriting ApiController
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
    );
}

And as a bonus, here's my RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute( //MapRoute for controllers inheriting from standard Controller
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

This has to be in that EXACT ORDER. I tried moving the calls around and ended up with either MVC, Api or Odata broken with 404 or 406 errors.

So I can call:

localhost:xxx/ -> leads to help pages (home controller, index page)

localhost:xxx/api/ -> leads to the OData $metadata

localhost:xxx/api/Sites -> leads to the Get method of my SitesController inheriting from ODataController

localhost:xxx/api/Test -> leads to the Get method of my TestController inheriting from ApiController.

Solution 2

If you are using OData V4, replace using System.Web.Http.OData;

With using Microsoft.AspNet.OData; (Please check the comments for the latest library)

in the ODataController works for me.

Solution 3

Set routePrefix to "api".

ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<CustomerType>("CustomerType");

config.MapODataServiceRoute(routeName: "ODataRoute", routePrefix: "api", model: builder.GetEdmModel());

Which OData version are you using? Check for correct namespaces, for OData V4 use System.Web.OData, for V3 System.Web.Http.OData. Namespaces used in controllers have to be consistent with the ones used in WebApiConfig.

Solution 4

My issue was related to returning the entity model instead of the model I exposed (builder.EntitySet<ProductModel>("Products");). Solution was to map entity to resource model.

Solution 5

Another thing to be taken into consideration is that the URL is case sensitive so:

localhost:xxx/api/Sites -> OK
localhost:xxx/api/sites -> HTTP 406

Share:
39,906

Related videos on Youtube

Ivan-Mark Debono
Author by

Ivan-Mark Debono

Updated on July 09, 2022

Comments

  • Ivan-Mark Debono
    Ivan-Mark Debono almost 2 years

    Before adding OData to my project, my routes where set up like this:

           config.Routes.MapHttpRoute(
                name: "ApiById",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional },
                constraints: new { id = @"^[0-9]+$" },
                handler: sessionHandler
            );
    
            config.Routes.MapHttpRoute(
                name: "ApiByAction",
                routeTemplate: "api/{controller}/{action}",
                defaults: new { action = "Get" },
                constraints: null,
                handler: sessionHandler
            );
    
            config.Routes.MapHttpRoute(
                name: "ApiByIdAction",
                routeTemplate: "api/{controller}/{id}/{action}",
                defaults: new { id = RouteParameter.Optional },
                constraints: new { id = @"^[0-9]+$" },
                handler: sessionHandler
    

    All controllers provide Get, Put (action name is Create), Patch (action name is Update) and Delete. As an example, the client uses these various standard url's for the CustomerType requests:

    string getUrl =  "api/CustomerType/{0}";
    string findUrl = "api/CustomerType/Find?param={0}";
    string createUrl = "api/CustomerType/Create";
    string updateUrl = "api/CustomerType/Update";
    string deleteUrl = "api/CustomerType/{0}/Delete";
    

    Then I added an OData controller with the same action names as my other Api controllers. I also added a new route:

            ODataConfig odataConfig = new ODataConfig();
    
            config.MapODataServiceRoute(
                routeName: "ODataRoute",
                routePrefix: null,
                model: odataConfig.GetEdmModel()
            );
    

    So far I changed nothing on the client side. When I send a request, I get a 406 Not Available error.

    Are the routes getting mixed up? How can I solve this?

    • gdoron is supporting Monica
      gdoron is supporting Monica over 9 years
      Add a prefix for your OData routes: routePrefix: "odata"
  • Karl Wenzel
    Karl Wenzel over 9 years
    The distinction between the System.Web.OData and System.Web.Http.OData namespaces for versions 3 and 4 was causing me a lot of grief. Note that this mistake resulted in a 406 Not Acceptable error in my case, as opposed to the 406 Not Available error that was reported by the OP.
  • Jerther
    Jerther about 9 years
    Wouldn't that yield a 404?
  • bkwdesign
    bkwdesign about 9 years
    I had a queryable 1st gen WebApi Controller (inhertied from ApiController). Went to upgrade it to Web Api 2 & got invalid cast exceptions due to the select+expand bug. Once I re-wrote my controller per sample #4 on this site I started to get 406 Not Acceptable errors. I was able to quiet these by once again inheriting from ApiController in my new project rather than inheriting from OdataController. Furthermore, I have no special odata route plumbling in my project.
  • Anton Kalcik
    Anton Kalcik about 9 years
    For me the hint with correct namespaces was correct. It seems that scaffolding function is using the V3 namespace System.Web.Http.OData instead of System.Web.OData
  • Carl Heinrich Hancke
    Carl Heinrich Hancke almost 9 years
    This was also a cause for my OData calls not working, but didn't lead to a 406, but as Jerther rightly said, a 404. Still, worth remembering when making the API calls that casing is important.
  • Alobidat
    Alobidat almost 9 years
    This is the solution if you have a correct mapping. Why it is referencing Systme.Web.Http.OData by default?
  • War
    War almost 9 years
    using System.Web.OData.* namespaces are for odata v4 endpoints and System.Web.Http.Odata.* is all the older odata v1-3 stuff. I had this problem too, poorly documented too as examples on ms sites often mix the two up.
  • Ege Aydın
    Ege Aydın over 8 years
    Yes you saved my day too
  • RKS
    RKS over 8 years
    I was tearing my hairs out in desperation when I saw your answer. Thanks a lot. :)
  • Parv Sharma
    Parv Sharma almost 8 years
    doing that alreay
  • Kye
    Kye over 7 years
    You sir are a hero!
  • BLogan
    BLogan almost 7 years
    That is very frustrating, but it ended up being my issue too - got 406 when the case was wrong.
  • dk_french032
    dk_french032 about 6 years
    Was my issue too - highly frustrating!
  • Christoph Herold
    Christoph Herold over 5 years
    I also got a 406, this was the fix for me.
  • Stefan Balan
    Stefan Balan over 5 years
    It can get even more stupid... I've spent a couple of days on this... return Ok(result); returns 406 if result is declared as object instead of EntityType...
  • Burst
    Burst over 5 years
    Now it's System.AspNet.OData
  • SLCH000
    SLCH000 about 5 years
    Now it's Microsoft.AspNet.OData
  • JeeShen Lee
    JeeShen Lee about 5 years
    Can anyone verify the changes suggested? Microsoft.AspNet.OData? I will edit my answer.
  • Bondolin
    Bondolin about 5 years
    See github.com/Microsoft/aspnet-api-versioning/issues/315: "With Microsoft.AspNet.OData v7.0, namespaces were changed from System.Web.OData to Microsoft.AspNet.OData."
  • Jingjing Zhong
    Jingjing Zhong over 4 years
    I didn't realize before that the "Products" should be the Controller prefix.. And also it's case senstive!
  • alamoot
    alamoot almost 4 years
    You're the real MVP!