Simple REST question with my MVC app

Last post 02-23-2008 1:42 PM by abombss. 6 replies.

Sort Posts:

  • Simple REST question with my MVC app

    02-22-2008, 11:20 AM
    • Loading...
    • jgo23
    • Joined on 12-06-2007, 5:52 PM
    • Posts 27

    I'm trying to keep my web app as RESTful as possible.. and one way I'm doing that is ridding my urls with any verbs (something I read about what a RESTful app is)

    I'm building the administration part of my site, so I've built an AdminController, which then maps to the following url:

    /Admin

    Within the admin (landing page, Index action), I will have several things to administer, one of them is, let's say, Groups.  Groups has become an action in my AdminController, which eventually maps to:

    /Admin/Groups

    The view page for Groups will display a list of groups, and each of which, I want to have the ability to either edit or delete.  The list of groups are not in a form and I just want links to do those actions.  I'm trying to figure out what the url should be for edit or delete... something like:

    /Admin/GroupEdit/1
    /Admin/GroupEdit/2
    etc.

    but now i have verbs in my url, which i want to avoid.  Or would the above urls still be considered RESTful?  Even if I went away from the AdminController and built a GroupController, the urls would still end up as:

    /Group/Edit/1
    /Group/Edit/2
    (which is similar to the way Scott Gu's tutorials have it)

    Would another option be to use forms with form's method = "delete" or "put" or something rather?  Or can the form's method be anything I want, other than the four that I've read (get, post, delete, put)?

    Thanks for any input.

  • Re: Simple REST question with my MVC app

    02-22-2008, 2:08 PM
    Answer

    http://www.codeplex.com/MVCContrib/Wiki/View.aspx?title=SimplyRestfulRouting&referringTitle=Documentation

    Looks like the SimplyRestfulRouteHandler might come in handy for you, which is part of MVCContrib. This gets around only having POST and GET from HTML forms using a _method=PUT and _method=DELETE request parameters.

    The next release of the MVC framework apparently has improved routing, giving more scope to build a clean REST approach. This also may allow Areas to be defined for Admin, a technique allowed in Castle Monorail.

    For the moment, yes, I'd recommend having a Controller per object ie. /Group/Edit/1 as you mention.

  • Re: Simple REST question with my MVC app

    02-22-2008, 2:56 PM
    • Loading...
    • jgo23
    • Joined on 12-06-2007, 5:52 PM
    • Posts 27

    Awesome! Thanks for the info.

    I also started thinking of a concept of "sub controllers" as well, so that a url that looked like:

    Admin/Groups/Edit/1

    would treat "Groups" as the actual controller.

    I started writing my own Handler and RouteHandler and then I realized I could've just done it with adding the appropriate entry in the Route table like so:

    Admin/Groups/[action]/[id]
    Default =  new { controller = "Groups" ...

    I'm waiting impatiently for the gu to release the next version... mix08 can't come any sooner!

  • Re: Simple REST question with my MVC app

    02-22-2008, 6:23 PM
    • Loading...
    • abombss
    • Joined on 06-27-2006, 12:13 PM
    • Chicago, IL
    • Posts 164

     I am waiting on the next set of bits to finish the simply restful routing stuff for ms mvc.  I am currently working a new better implementation for monorail which I will port over to contrib when its ready.  The biggest up side will be rule definition sets get put into a container and the container will sort, organize, and build the routing rules for you.  This accomplishes a couple things.  First you no longer have to manually maintain your routing rule order, the builder will figure out the correct order from the most specific to the most generic.  Secondly this will handle nested controllers which is currently a huge pain point for me.  And finally, I do enumerate all the controllers in the application so your most generic rule <controller>/<action> will not match nested controllers and controllers in "areas".  So you won't be able to accidentally go to http://localhost/roles/123/edit when RolesController is defined in the admin area.

    So manually creating the route with the builder could look something like this.

    routingBuilder.NewSimplyRestfulRouteForController<ContactMethodsController>()
    .FromSimplyRestfulParentController<PeopleController>()
    .ToController()
    .ToRequiredString("type")
    .AcceptingValues("phone","email","address")
    .Register();

    which make the following url: http://localhost/people/123/contact-methods/phone/456/edit match the Edit action on the ContactMethodsController with parameters for personId, contactMethodId, and type.

    I will also offer support for using attributes on your controllers to achieve registration as well, assuming the order you define attributes is respected by the compiler, otherwise the syntax will get ugly.  

    [SimplyRestful,
    RestfulParent(typeof(PeopleController)),
    RestfulController,
    RequiredString("type", new string[] {"phone", "email", "address"})]
    public class ContactMethodsController : SimplyRestfulBaseController
    {
    public void Edit(int personId, string type, int contactMethodId)
    {
    }

    /// matches http://localhost/people/123/contact-methods/phone/search?q=555 [ListingAction] public void Search(string type, string q)
    {
    }

    /// matches http://localhost/people/123/contact-methods/email/456/detail [EntityAction] public void Detail(int personId, string type, int contactMethodId)
    {
    }
    }

    routingBuilder.RegisterRoutesFromAssembly<HomeController>();
    routingBuilder.BuildRoutes(routeCollection);
     
    I have a good bit already worked out for monorail for one of my current projects.  There are still some quirks but I am liking it.  I really look forward to see what is in store in the next ctp.
    Adam Tybor -- abombss.com
    Filed under: , ,
  • Re: Simple REST question with my MVC app

    02-23-2008, 8:21 AM

    I think this looks amazingly powerful. As you mention there have been hints that REST might be easier with the newer CTP. But I think the parent/child controller conventions may have to come from the community.

    Though I'm a little confused with your sample - is contactMethodId a unique id for this ContactMethod entity or is it a composite key/query requiring both personId and contactMethodId?

    I've been looking at some of the articles on Nested Resources eg. http://a-simian-mind.blogspot.com/2008/02/nested-resources-in-aspnet-mvc.html,  http://adam.blog.heroku.com/past/2007/12/20/nested_resources_in_rails_2/ and http://benyork.com/BenYork/blogs/dotnet/archive/2008/01/23/nested-resources-and-html-form-microsoft-mvc.aspx.

    Is there some way of using Lambda expressions to resolve the value for personId, so that it doesn't have to be a parameter in the ContactMethod Detail action? (So that this controller could also optionally be used without the full REST URL eg. standard /[controller]/[action]/[id] and this nested approach, as a child of PeopleController).

    I've made a stab at some pseudo code below. Sorry if it makes no sense. Or am I getting wrong the type of nested resource you are wanting?

    routingBuilder.NewSimplyRestfulRouteForController<ContactMethodsController, ContactMethod>()
    .FromSimplyRestfulParentController<PeopleController, Person>()
    .Where(p => ContactMethodsService.GetPersonFromContactMethod(c))


    .ToController()
    .ToRequiredString("type")
    .AcceptingValues("phone","email","address")
    .Register();
    [EntityAction] /// personId removed
    public void Detail(string type, int contactMethodId) { }
    On a slightly different angle, I'm increasingly looking at REST and AtomPub. So I like the direction of the [ListAction] and [EntityAction] attributes. I suppose for an AtomPub version it could have the likes of [EntryAction], [MediaAction] (so that a REST controller can also handle binary uploads, following AtomPub protocol), [FeedAction], [ServiceAction] (iterate over actions and controllers to autogenerate a list of feeds/actions following AtomPub spec) etc.

     

  • Re: Simple REST question with my MVC app

    02-23-2008, 1:25 PM
    • Loading...
    • abombss
    • Joined on 06-27-2006, 12:13 PM
    • Chicago, IL
    • Posts 164

    andyfreestyle:
    Though I'm a little confused with your sample - is contactMethodId a unique id for this ContactMethod entity or is it a composite key/query requiring both personId and contactMethodId?
     

    Well, the example showed what parameters are actually getting captured from the url in the route.  You don't need to put them all in if you don't need them.  If contactMethodId is a primarykey do you really need the personId?  Probably not, you could just load the ContactMethod by the contactMethodId and grab the ref to its person.  I personally use NHibernate and I don't pass primary keys around.  I pass the objects.  So some pseudo code for my class would look like this.

    [SimplyRestful,
    RestfulParent(typeof(PeopleController)),
    RestfulController,
    RequiredString("type", new string[] {"phone", "email", "address"}),
    EntityFetchStrategy(typeof(ContactMethodWithPersonStrategy)]
    public class ContactMethodsController : SimplyRestfulBaseController
    {
    public void Edit([EntityParam] ContactMethod contactMethod)
    {
    }

    /// matches http://localhost/people/123/contact-methods/phone/search?q=555 [ListingAction] public void Search(string type, [GetParam("q")]string query)
    {
    }

    /// matches http://localhost/people/123/contact-methods/email/456/detail [EntityAction] public void Detail([EntityParam]ContactMethod contactMethod)
    {
    }
    }

    andyfreestyle:
    Is there some way of using Lambda expressions to resolve the value for personId, so that it doesn't have to be a parameter in the ContactMethod Detail action? (So that this controller could also optionally be used without the full REST URL eg. standard /[controller]/[action]/[id] and this nested approach, as a child of PeopleController).
     

    Currently no... My understanding of a nested controller is that it is nested under an entity, am I wrong on that? Could you provide an example where a nested controller would operate on something without a resource to its parent entity? I am not sure what kind of route your pseudo code would match? If you don't want the fully restful nested routing, don't make it a nested controller

    andyfreestyle:
    On a slightly different angle, I'm increasingly looking at REST and AtomPub.
     

    I haven't looked at it yet, but it could certainly be possible.  I would think though that you would just use a different view with a different url extension.  So for the atom feed it be /people.atom and for the entry it would be /people/123.atom

    andyfreestyle:
    o I like the direction of the [ListAction] and [EntityAction] attributes.
     

    While I try and keep my controllers limited to the primary restful actions its inevitable that you will need some other custom actions.

    Adam Tybor -- abombss.com
  • Re: Simple REST question with my MVC app

    02-23-2008, 1:42 PM
    • Loading...
    • abombss
    • Joined on 06-27-2006, 12:13 PM
    • Chicago, IL
    • Posts 164

    Ah Andy, now I see what you wanted.  You want the route to be both nested and default.  So maybe we could do something like this. Notice the OptionalRestfulParent

    [SimplyRestful,
    OptionalRestfulParent(typeof(PeopleController)),
    RestfulController,
    RequiredString("type", new string[] {"phone", "email", "address"}),
    EntityFetchStrategy(typeof(ContectMethodWithPersonStrategy)]
    public class ContactMethodsController : SimplyRestfulBaseController
    {
    }
     

     To use that you could probably do one of two things.  Have two sets of overloads, one with the parentId and one without.  Or keep the most general action method with all the params and the parent param would be null if the url came in directly without a parent.  I am sure this is possible... I don't need it and I think its weird, as I really can't imagine wanting or needing this type of functionality.  What I see a need for more would be to have a single controller be nested for muliple parents.

    Lets say you have a Contact and Task that both can have collection of Notes.  You may want to define a single NotesController that can be accessible from both /contact/123/notes and /task/123/notes.  That is more valuable to me at the moment.  But the best part of oss is you can patch it :)

     

    Adam Tybor -- abombss.com
Page 1 of 1 (7 items)
Microsoft Communities
Page view counter