Showing posts with label mvc. Show all posts
Showing posts with label mvc. Show all posts

Friday, November 28, 2014

ASP.Net: Web API 2 + Help Pages + OData

Doing each of them separately is fairly easy and there's lots of tutorials on how to do it. But as for mixing them together, I've not seen much. Some blogs claim it's not possible and buggy and mostly lead to 404 and 406 errors. Well, out of luck, I made it work. The secret is in the ROUTING!

I don't understand all the concepts behind this and I believe most of us don't, and don't care to. Still, feel free to explain the unexplained in the comments.

So, I started with a ASP.Net Application project. I Selected Web API for the template so I could have the help pages ready. Then I added the needed stuff for OData V4 following this really nice tutorial. Last, I had to configure the ROUTES, and that is where all hell resides (well, that's what all the blog posts and obscure SO questions lead me to think). THe following has been taken from my SO answer.

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

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

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.