HTTP 406 when mixing WebAPI RouteTable.Routes.MapHttpRoute and HttpConfiguration.Routes.MapODataRoute

[UPDATE] 2014-11-28
Jerther sent me a comment (see below) on how he solved this issue! So how did he do it? The short answer is, that you have to include special registration code inside the ‘Application_Start()’ and other places where the order on how you code it plays a crucial role. Check out his post ASP.Net: Web API 2 + Help Pages + OData to get a full example and further explanation.

Recently I observed a strange behaviour when I added an ODATA WebAPI controller to a working WebAPI configuration. As I found the use case not too uncommon and as it creates some tricky side effects I decided to share this bit of information with you.

The issue in short: when you mix MapHttpRoute and MapODataRoute registrations for a ‘regular’ controller (pattern: ‘{controller}/{key}’) you have controller2 also available via URL of controller1.

Now here the configuration in a little bit more detail:
1.With the below configuration I have two WebAPI Controllers that expose their functionality under “/api/helper”. These controllers work just fine.
Their route is registered via “Routes.MapHttpRoute”.
2.The Odata Controller at the end (which I registered separately in WebApiConfig) has a different route via “/odataapi”.
This route is registered via “HttpConfiguration.Routes.MapODataRoute”.

When I query “/api/helper/EntitySetOdatas” (and not “/odataapi/EntitySetOdatas” the complete GetEntitySetOdatas() from the ODATA controller is being executed but upon return errors with an HTTP 406 “Not Acceptable” (see Fiddler output at the end).

So the question is: why is the Odata controller code executed at all, as the URI/path is actually “wrong”? I would expect that the controller is only being called when the URI is on the form “/odataapi/EntitySetOdatas”?

Maybe it is a bug? Or is it not allowed/not supported to mix different route specifications (RouteTable.Routes.MapHttpRoute and HttpConfiguration.Routes.MapODataRoute)? I actually do not know. However, I wanted to make you aware of this issue.

The reason why this is not only particular annoying but might also pose a security risk is, that you cannot just put a reverse proxy in front of it and allow certain URLs/controllers from external sources and other only from internal sources. The code gets executed nevertheless. In our case he had an ODATA endpoint accepting POST request (intentionally had to be unauthenticated) that should not have been available for specific sources. Unfortunately this does not work as it turns out, as the code is still reachable via the path from controller1.

Further information: the issue occurred using WebApi 5.2.0 (using the deprecated methods for the ODATA configuration). the issue has been posted to HTTP 406 when mixing WebAPI RouteTable.Routes.MapHttpRoute and HttpConfiguration.Routes.MapODataRoute.

public class Global : System.Web.HttpApplication
{
  protected void Application_Start(object sender, EventArgs e)
  {
    RouteTable.Routes.MapHttpRoute("SvcWebApi1", "api/helper/{controller}");
    RouteTable.Routes.MapHttpRoute(
      name: "SvcWebApi2",
      routeTemplate: "api/helper/{controller}/{id}/{action}",
      defaults: new { id = RouteParameter.Optional, action = "default" }
      );
    GlobalConfiguration.Configure(WebApiConfig.Register);

  }
}

public static class WebApiConfig
{
  public static void Register(HttpConfiguration config)
  {
    config.Routes.MapODataRoute(
      routeName: "odataapi"
      ,
      routePrefix: "odataapi"
      ,
      model: GetModel()
      ,
      batchHandler: new DefaultODataBatchHandler(GlobalConfiguration.DefaultServer)
    );
    config.MapHttpAttributeRoutes();
    config.EnableQuerySupport();
  }
  private static IEdmModel GetModel()
  {
    ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
    builder.EntitySet<LightSwitchApplication.Models.EntitySetOdata>("EntitySetOdatas");

    return builder.GetEdmModel();
  }
}
GET http://www.example.com:80/api/helper/EntitySetOdatas/ HTTP/1.1
MaxDataServiceVersion: 3.0
Accept: application/xml
Referer: http://www.example.com:80/Application/
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
Host: www.example.com:80
Cookie: msls-client-parameters=preferredLanguage=en-US

HTTP/1.1 406 Not Acceptable
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/8.0
X-AspNet-Version: 4.0.30319
X-Content-Type-Options: nosniff
Persistent-Auth: true
X-Powered-By: ASP.NET
Date: Mon, 28 Jul 2014 01:28:34 GMT
Content-Length: 0

Comments

  1. L.H. says:

    same problem using the new config.MapODataServiceRoute mehtod.

    • Ronald Rink says:

      Thanks for sharing this information! Do you have any idea or explanation for this?

  2. Reblogged this on Dinesh Ram Kali..

  3. I made it. Almost out of pure luck, but mostly out of perseverance:
    http://jerther.blogspot.ca/2014/11/aspnet-web-api-2-help-pages-odata_28.html

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: