API routing 404 if optional parameter missed
-
21-12-2019 - |
Question
I have this routing set up in my WebApiConfig
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
and these methods inside /Api/ProductController.cs
public object GetProduct(int id)
{
var productRepo = new ProductRepository(db);
var product = productRepo.GetItem(id);
return product.ToAnonymousType();
}
public object GetTest()
{
var productRepo = new ProductRepository(this.db);
var product = productRepo.GetItem(10000);
return product.ToAnonymousType();
}
I can request /api/product/getproduct/10000 and get JSON back.
If I request /api/product/gettest I get a 404
If I change gettest to
public object GetTest(int id)
and request /api/product/gettest/10000 I get JSON.
I have no idea why that 404 is happening. I have other controllers (for non-api requests) that are handling requests as normal. I'm new to MVC after some years of webforms, and this has me stumped. For reference, here's my RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
and my Application_Start
protected void Application_Start()
{
BundleConfig.RegisterBundles(BundleTable.Bundles);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
}
Solution
This is due to the ordering of your routes.
RouteConfig.RegisterRoutes(RouteTable.Routes);
is called before
GlobalConfiguration.Configure(WebApiConfig.Register);
and your RegisterRoutes method contains a default (and very generic) route
url: "{controller}/{action}/{id}"
this generic (default) rule will match your url pattern of
api/product/gettest
before your WebAPI route will get a chance to match it.
You can either get rid of that super generic route if not needed (and use more specific routes), or simply move the call to
GlobalConfiguration.Configure(WebApiConfig.Register);
to come before the call to
RouteConfig.RegisterRoutes(RouteTable.Routes);
in your Global.asax.
I built a sample with all of the info you gave, and moving that call worked just fine. I hope this helps.