Search code examples
asp.net-web-apiasp.net-mvc-areashttp-status-code-405

Why does my Web API return 405 for everything?


I have a ASP.NET Web API 2 controller in a custom Area. (I also have some API controllers in the default route)

I have registered the route:

// Web API routes
config.MapHttpAttributeRoutes();

// NEW ROUTE FOR AREAS
config.Routes.MapHttpRoute(
    name: "API Area MyArea",
    routeTemplate: "api/MyArea/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional });

My controller and method:

 [RoutePrefix("myarea/api/accountapi")]
  public class AccountAPIController : ApiController
  {
...

    [System.Web.Http.HttpGet]
    [AcceptVerbs("GET")]
    [AllowAnonymous]
    [Route("emailexists")]
    public async Task<IHttpActionResult> EmailExsists([FromUri]string email)
    {
      var user = await UserManager.FindByEmailAsync(email).ConfigureAwait(false);
      return Ok(user != null);
    }
...
}

But I can't get this to work, no matter what I try I get: https://localhost:44300/api/accountapi/emailexists?email=info@something.com

{"message":"The requested resource does not support http method 'GET'."}

In fact, I can't get it to return an expected 404. For example, this: https://localhost:44300/api/accountapi/jibberish

Also returns a 405.

My API controllers in the default route works as expected, i.e. returning a 404 when it should.

Any ideas ?


Solution

  • Let's analyze the endpoints you are calling.

    The first URI:

    https://localhost:44300/api/accountapi/emailexists?email=info@something.com
    

    Looking at your routing configuration this will match only api/{controller}/{id} route template. That's because the first convention routing config will only match URI parts starting with api/MyArea, while the attribute routing prefix of your controller will only match paths starting with myarea/api/accountapi).

    This will translate in the following route attributes:

    • {controller}: accountapi
    • {id}: emailexists
    • ?email: info@something.com (this will be ignored for routing purposes because is not included in the template)

    Looking at your AccountAPIController I can't see any Action that allows GET and also has a string parameter called (explicitly) id: thus a 405 is returned.

    The second URI:

    https://localhost:44300/api/accountapi/jibberish
    

    Will again only match the same route template as before:

    • {controller}: accountapi
    • {id}: jibberish

    For the same reason a 405 response is returned.

    What to do?

    Your main problem is that you are mixing different routing methods without correctly matching the URI for your action. Without modifying the current route configuration the correct address for your EmailExsists method is the following one:

    https://localhost:44300/myarea/api/accountapi/emailexists?email=info@something.com
    

    This will match your RoutePrefix and Route attributes in the AccountAPIController.

    The following URIs should also match your action method based on the two routing convention you configured (they should be equivalent):

    https://localhost:44300/api/accountapi?email=info@something.com
    https://localhost:44300/api/MyArea/accountapi?email=info@something.com
    

    My suggestion is to avoid the use of too many different routing templates and configurations: if you need the maximum flexibility use only attribute routing and remove the convention-based routing configuration.