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