ProfK ProfK - 12 days ago 7
C# Question

Why do I get an HTTP 404 on this one Web API request?

I am making a

POST
request, and getting a
404 - Not Found
error back, to this controller and action:

[AllowAnonymous]
[System.Web.Mvc.RoutePrefix("api/Appt")]
public class AppointmentController : BaseController
{
[HttpPost]
[Route("")]
public AppointmentDto Post(AppointmentDto model)
{
Db.Appointments.Add(model);
Db.SaveChanges();
Logger.Info($"Appointment ID {model.Id} created.");
return model;
}
}


The request is made from a WPF client using
HttpClient
from the
Microsoft.AspNet.WebApi.Client
package. The client is configured like so:

public abstract class BaseRestClient
{
protected const string BaseAddress = "http://localhost:51009";
protected HttpClient Client;

protected virtual void ConfigureClient()
{
Client = new HttpClient { BaseAddress = new Uri(BaseAddress) };
Client.DefaultRequestHeaders.Clear();
Client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}


And called as follows:

var response = await Client.PostAsJsonAsync("api/Appt", model, cancellationToken);


Properties on
response
include:

StatusCode: 404`
ReasonPhrase: 'Not Found'`
RequestMessage:
{Method: POST, RequestUri: 'http://localhost:51009/api/Appt'`


This is the only request I'm making to a
POST
action, with a
model
parameter, but, ironically, a
POST
request to a
GET
action on an almost identical controller works fine. With the controller:

[System.Web.Mvc.RoutePrefix("api/Person")]
public class PersonController : BaseController
{
[HttpPost]
public async Task<IEnumerable<PersonDto>> Get()
{
Db.Configuration.LazyLoadingEnabled = false;
return await Db.Persons.ToListAsync();
}
}


the request made as below works fine and returns all my
Person
objects:

HttpResponseMessage response = await Client.PostAsync("api/Person", null, cancellationToken);


Two similar requests to
GET
actions also work perfectly.

So why would the one resource be found, and the other not? Are the any other, hidden reasons a 404 would be returned other than the requested resource not being found?

Could this be due to a conflict with two types of routing, e.g.

config.MapHttpAttributeRoutes();

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


I have to use attribute routing in one place, the comment explains why:

[HttpPost]
[Route("Get")]
// NOTE HTTP 405 - Method not allowed when this action is named 'Get'.
public async Task<IEnumerable<BranchDto>> Fetch()
{
Db.Configuration.LazyLoadingEnabled = false;
return await Db.Branches.ToListAsync();
}

Answer

You are using the wrong route prefix attribute.

System.Web.Mvc.RoutePrefix("api/Appt")

is for MVC not Web API.

You need the one in System.Web.Http

System.Web.Http.RoutePrefix("api/Appt")

The reason the person controller example worked is because it defaulted back to convention-based routing.

original would have worked if you called POST http://localhost:51009/api/Appointment via convention-based routing.