Steve Michelotti

A .NET Developer's Toolbox

  Home  |   Contact  |   Syndication    |   Login
  199 Posts | 0 Stories | 1106 Comments | 51 Trackbacks

News

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

What I'm Reading:

Shelfari: Book reviews on your book blog

Tag Cloud


Archives

Post Categories

Code

Publications

Recently I blogged about WCF REST services with no svc file and no config. In this post I also discussed the pros/cons of WCF services as compared to using MVC controller actions for web services and I made the case that, in many instances, WCF REST services is better than using the MVC infrastructure because WCF provides:

  • a more RESTful API with less work
  • a convenient automatic help page to assist consumers of your service
  • automatic format selection (i.e., xml/json) depending on HTTP headers

In any case, using both WCF REST services and MVC-style web services are *both* quite convenient – and you don’t have to choose. You can use both. In fact, you can use both inside the same web application project. However, a recent question on StackOverflow illustrates that you have to be careful how you set up your routing in order to make WCF REST services work inside MVC applications.

If you have a look at the question on StackOverflow, you’ll see the routes were originally set up like this:

   1:  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   2:              
   3:  routes.Add(new ServiceRoute("Person", new WebServiceHostFactory(), typeof(PersonService)));
   4:   
   5:  routes.MapRoute(
   6:      "Default", // Route name
   7:      "{controller}/{action}/{id}", // URL with parameters
   8:      new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
   9:  );

The service route was for the WCF Service (using no *.svc file) and the last route was the standard MVC route. In most of the cases this works fine. Navigation to the root correctly lands on the HomeController’s Index method. Navigation to “/Person/help” correctly goes to the WCF REST help page and the WCF REST service can be invoked just fine. However, the action links were not correct.  When formulating a simple action link like this:

   1:  <%: Html.ActionLink("Home", "Index", "Home")%>

The resulting URL being produced was: “/Person?action=Index&controller=Home” which was clearly not correct. It’s hitting the first route and thinking it should just put “Person” for the first segment. It then cannot match the “controller” or “action” tokens so it just puts them in the query string. It will attempt to go to the service but the URI is incorrect so, if it’s a WCF REST service, you’ll just get the default help page.

Of course, we know that the *order* matters for routing so what if we put the MVC route first and the service route second? In this case, we run into a different problem. If the first URI segment is “Person” then it will try to match that as the controller name and you’ll get a 404 or, if you’re using a controller factory, your IoC container won’t be able to find a controller named “PersonController”.

So how can we get the WCF REST service route and the MVC routes to play nice with each other? The simple answer: use a route contraint:

   1:  routes.MapRoute(
   2:      "Default", // Route name
   3:      "{controller}/{action}/{id}", // URL with parameters
   4:      new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
   5:      new { controller = "^(?!Person).*" }
   6:  );
   7:   
   8:  routes.Add(new ServiceRoute("Person", new WebServiceHostFactory(), typeof(PersonService)));

Notice on line #5 above, we add a route constraint so that it will try to use the MVC route first as long as the first segment (where the “controller” token is positioned) is not the “Person” string which matches where we want our WCF REST service to be. If it is “Person”, then it will fall through to the service route and correctly point to our WCF REST service.

One other interesting note when using service routes in MVC applications – anytime you’re going beyond the default MVC routes, I highly recommend you unit test your routes. This can be done very easily with the TestHelper that comes with MvcContrib. It makes it trivial to unit test routes like this:

   1:  [TestMethod]
   2:  public void default_path_should_be_able_to_match_controller()
   3:  {
   4:      "~/".Route().ShouldMapTo<HomeController>();            
   5:  }

The unfortunate caveat here is that you cannot use this when you’re combining service routes. If you do, you’ll get this exception when you initialize your routes at the start of the unit test by invoking the static RegisterRoutes() method: “System.InvalidOperationException: System.InvalidOperationException: 'ServiceHostingEnvironment.EnsureServiceAvailable' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS.” Fortunately, we can still use Phil Haack’s Route Debugger:

 wcf route debug

 

With the URL of “/Person/23”, it correctly matches my service route for my WCF service.

WCF REST and MVC are both great and they should be used *together* when appropriate.

posted on Wednesday, September 22, 2010 10:55 PM

Feedback

# re: WCF REST Services Inside MVC Projects 9/27/2010 6:02 PM Arsalan Ahmed
This post has been quite informative. It makes sense to use WCF for RESTful services because it requires slightly less work than using MVC. I would argue that for a limited and simple API on a small project especially for internal use, it may be simpler in an MVC project to offer REST services than using WCF. In the end, it's a toss up. I would go with WCF for large scale API design.

Question: Could you elaborate on "The unfortunate caveat here is that you cannot use this when you’re combining service routes" ?

# re: WCF REST Services Inside MVC Projects 9/28/2010 8:34 PM Steve
@Arsalan - I was referring to the MvcContrib route testing API: Route().ShouldMapTo<HomeController>(). You can only use that when using normal MVC routes. If you've configured your route table to include service routes as well, you'll get the error I showed above.

# re: WCF REST Services Inside MVC Projects 1/9/2011 12:18 AM Gustyn
Love the article. Exactly what I am looking for. Now, the second part of my problem is whether you can use the membership methods of the mvc architecture with the WCF services.

# re: WCF REST Services Inside MVC Projects 1/16/2011 8:04 PM KG
Awesome post, thank you!

# re: WCF REST Services Inside MVC Projects 2/4/2011 10:28 PM Steve
@Gustyn - Have a look at this post: http://weblogs.asp.net/cibrax/archive/2011/02/04/authenticating-clients-in-the-new-wcf-http-stack.aspx

# re: WCF REST Services Inside MVC Projects 4/13/2011 7:35 AM matt
It is great to hear that they can both exist... I am starting a project that seems the integration of the two would make se
Sense... Thanks for the great post.


# re: WCF REST Services Inside MVC Projects 2/10/2012 9:18 PM Raj
Cannot thank you enough!

Post A Comment
Title:
Name:
Email:
Comment:
Verification: