Reputation: 2919
I am using vs2012 and started a new project with "ASP.net Web Api 2" template. Finished coding and started to test the system. I observe strange behaviour with routing. Such as:
Here are my routes:
config.Routes.MapHttpRoute(
name: "personRoute",
routeTemplate: "api/px/{action}/{personid:Guid}",
defaults: new { controller = "px", personid = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and here is my controller:
public class PxController : ApiController {
private Configuration.DatabaseContext dbctx = new Configuration.DatabaseContext();
[HttpGet]
public IEnumerable<Models.Person> GetAll() {
return dbctx.Persons.ToArray();
}
[ActionName( "op" )]
[HttpGet]
public Models.Person ById( Guid personid ) {
var data = dbctx.Persons.FirstOrDefault( e => e.PersonId == personid );
if (data == null) {
throw new HttpResponseException( HttpStatusCode.NotFound );
}
return data;
}
[ActionName( "op" )]
[HttpPost]
public void Insert( [FromBody] Models.Person newPerson ) {
dbctx.Persons.Add( newPerson );
dbctx.SaveChanges();
}
[ActionName( "op" )]
[HttpPut]
public void Update( [FromBody]Models.Person eperson ) {
var data = dbctx.Persons.FirstOrDefault( e => e.PersonId == eperson.PersonId );
if (data == null) {
throw new HttpResponseException( HttpStatusCode.NotFound );
}
dbctx.Entry( eperson ).State = System.Data.Entity.EntityState.Modified;
dbctx.SaveChanges();
}
[ActionName( "op" )]
[HttpDelete]
public void DeleteById( Guid personid ) {
var data = dbctx.Persons.FirstOrDefault( e => e.PersonId == personid );
if (data == null) {
throw new HttpResponseException( HttpStatusCode.NotFound );
}
dbctx.Entry( data ).State = System.Data.Entity.EntityState.Deleted;
dbctx.SaveChanges();
}
#region |:.Extended Queries.:|
[HttpGet]
public IEnumerable<RemoteProperty> Properties( Guid personid ) {
var data = from n in dbctx.RemoteProperties
where n.PersonId == personid && n.ParentId == null
orderby n.PropertyType.Name
select n;
if (data == null)
throw new HttpResponseException( HttpStatusCode.NotFound );
foreach (var rp in data) {
rp.Details = dbctx.RemoteProperties.SqlQuery( "SELECT * FROM properties WHERE parentid = @parid" ).ToArray();
}
return data.ToArray();
}
[HttpGet]
public IEnumerable<Relation> Relations( Guid personid ) {
var data = from n in dbctx.Relations
where n.PersonId == personid
select n;
if (data == null)
throw new HttpResponseException( HttpStatusCode.NotFound );
return data.ToArray();
}
#endregion
[NonAction]
protected override void Dispose( bool disposing ) {
if (dbctx != null && dbctx is IDisposable) {
dbctx.Dispose();
}
base.Dispose( disposing );
}
}
These are the Fiddler outputs for the queries:
http://localhost:49318/api/px/GetAll
/* Works as expected */
http://localhost:49318/api/px/op/dfc737ca-312c-e411-ae3c-78843ccba6ef
/* The requested resource does not support http method 'GET'. */
http://localhost:49318/api/px/op?personid=dfc737ca-312c-e411-ae3c-78843ccba6ef
/* Returns the data with personid, but why the above query fails? */
http://localhost:49318/api/px/properties?personid=dfc737ca-312c-e411-ae3c-78843ccba6ef
/* Returns the data with personid */
http://localhost:49318/api/px/properties/dfc737ca-312c-e411-ae3c-78843ccba6ef
/* No action was found on the controller 'Px' that matches the request */
My question is what could be the reason for this behaviour? I also cannot use routes like (because they are not working):
"api/{controller}/{personid}/{action}"
Upvotes: 0
Views: 262
Reputation: 2919
Ok. By examining the whole project and applying the changes (listed below), I successfully managed to have it worked. So here is what I did:
For debugging the project, there is a "trace" assembly which MS provided, I used it. It puts trace messages to output window what is being done while routing and more. So I was able to see what is (was) happening. VS 2012 update is not enough to make it work.
Upvotes: 0
Reputation: 313
Try using the [Route] Attribute instead of the [Action] attribute.
On my controllers I have been successfully using the following:
[RoutePrefix("api/Demo")] // Sets the base route for actions in this controller
public class DemoController : ApiController
{
[HttpGet]
[Route("DemoAction")] // This makes this function map to route http://site/api/Demo/DemoAction
public IHttpActionResult PerformComplexApiAction(int id)
{
...
The [RoutePrefix] can be omitted and the full route can be provided in the individual [Route] entry on each function.
Upvotes: 1