3

I am trying to make hierarchical link in my RESTapi. For example:

Following url will give me details of actor id 1:

/api/v1/actor/id/1/

Following url is expected to give me all movies of actor id 1:

/api/v1/actor/1/movies

My routes:

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

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

My Actions in ActorController:

        [HttpGet]
        public HttpResponseMessage Id(int id)
        {
            // logic

            return Request.CreateResponse(HttpStatusCode.OK, actor);
        }

        [HttpGet]
        public HttpResponseMessage Movies(int levelOneId)
        {
            // logic
            return Request.CreateResponse(HttpStatusCode.OK, movies);
        }

But this setup is not working for me.

/api/v1/actor/id/1/ gives me proper response

But /api/v1/actor/1/movies is throwing following error:

No action was found on the controller 'Actor' that matches the name '1'."

I did follow this thread, but it did not work for me.

Can some please suggest what wrong I am doing here? I am using MVC 4, WebAPI.

Community
  • 1
  • 1
Arry
  • 1,630
  • 9
  • 27
  • 46

3 Answers3

1

Try switching the order of your routes:

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

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

Order matters when creating routes. The way you have it, it's matching the first one (the default route) first, and not finding an action with the name of "1".

You might also want to look into MVC Attribute routing, I think it's a tad easier to work with: http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx

BFree
  • 102,548
  • 21
  • 159
  • 201
  • Thanks for your answer @BFree . I already tried it, and it does not work. If I switch the order, the other route stops working. I mean this one /api/v1/actor/id/1/ – Arry Aug 28 '15 at 21:50
  • Ah, I misread your question. If you want to follow REST, you should have your route be: `/api/v1/actor/1` and then change your `Id` action to just be called `Get`. That should work with the route configuration I laid out. – BFree Aug 28 '15 at 21:53
  • This is because both urls match both routes. See my comment on the question. I'll post it as an answer if it suits your needs. – Mihai Caracostea Aug 28 '15 at 21:53
  • @BFree: I did change Id to Get(int id), but it didn't work. Changing to just Get() will also not work as I need id, like this /api/v1/actor/1 Can you please explain your solution a bit? – Arry Aug 28 '15 at 22:00
  • 1
    Use attribute routing and you'll have less problems. Remember to use parameter constraints. – Carles Company Aug 31 '15 at 20:10
1

This work around did thing for me:

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


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

Basically I have added 1 more path level to my url. It finally became:

/api/v1/actor/4/details/movies
Arry
  • 1,630
  • 9
  • 27
  • 46
0

Use these routes:

config.Routes.MapHttpRoute(
   name: "DefaultOneLevelNested", 
   routeTemplate: "api/v1/{controller}/{id}/{action}", 
   constraints: new { controller = "actor", action ="movies" }
);

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

They should match your initial urls.

Mihai Caracostea
  • 8,336
  • 4
  • 27
  • 46
  • Thanks @Mihai for your answer. With your solution this worked: api/v1/actor/1/movies But /api/v1/actor/id/1/ is still giving error: No action was found on the controller – Arry Aug 28 '15 at 22:05
  • /api/v1/actor/1 should also work and get routed to the Id action. – Mihai Caracostea Aug 28 '15 at 22:06
  • Just make sure you've removed the other routes. – Mihai Caracostea Aug 28 '15 at 22:06
  • Removed, but still same error for /api/v1/actor/id/1/ No action was found on the controller 'Actor' that matches the name '1'. Final route: config.Routes.MapHttpRoute( name: "DefaultOneLevelNested", routeTemplate: "api/v1/{controller}/{id}/{action}", defaults: new { id = "id" } ); – Arry Aug 28 '15 at 22:10
  • Use /api/v1/actor/1 or /api/v1/actor/1/id You said the first version was acceptable. – Mihai Caracostea Aug 28 '15 at 22:12
  • /api/v1/actor/1 this will not work as I have other actions as well. It gives "Multiple actions were found that match the request". I am forced to stick with /api/v1/actor/1/id – Arry Aug 28 '15 at 22:16
  • And that is why I need this structure, so that I can avoid collision between same parameter type actions: /api/v1/actor/id/1/ I can also specify /api/v1/actor/born/1966/ – Arry Aug 28 '15 at 22:17
  • Those two urls will work with this template "api/v1/{controller}/{action}/{id}" – Mihai Caracostea Aug 28 '15 at 22:22
  • :That's exactly what I have in my problem :) And it does not work. May be you can post both the routes again, so that I can try as per your say from scratch. I will share the results then. – Arry Aug 28 '15 at 22:36