Overriding a .svc Request With Routing

I was drawn to an interesting question on StackOverflow recently about how to override a request for a non-existent .svc request using routing.

One useful feature of routing in ASP.NET is that requests for files that exist on disk are ignored by routing. Thus requests for static files and for .aspx and .svc files don’t run through the routing system.

In this particular scenario, the developer wanted to replace an existing .svc service with a call to an ASP.NET MVC controller. So he deletes the .svc file and adds the following route:

routes.MapRoute(
  "UpdateItemApi",
  "Services/api.svc/UpdateItem",
  new { controller = "LegacyApi", action = "UpdateItem" }
);

Since api.svc is not a physical file on disk, at first glance, this should work just fine. But I tried it out myself with a brand new project, and sure enough, it doesn’t work.

Baffling!

So I started digging into it. First, I looked in event viewer and saw the following exception.

System.ServiceModel.EndpointNotFoundException: The service '/Services/api.svc' does not exist.

Ok, so there’s probably something special about the .svc file extension. So I opened up the machine web.config file located here on my machine:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config

And I found this interesting entry within the buildProviders section.

<add extension=".svc" 
  type="System.ServiceModel.Activation.ServiceBuildProvider, 
  System.ServiceModel.Activation,
  Version=4.0.0.0, Culture=neutral, 
  PublicKeyToken=31bf3856ad364e35" 
/>

Ah! There’s a default build provider registered for the .svc extension. And as we all know, build providers allow for runtime compilation of requests for ASP.NET files and occur very early in response to a request.

The fix I came up with was to simply remove this registration within my application’s web.config file.

  <system.web>
    <compilation debug="true" targetFramework="4.0">
      <buildProviders>
        <remove extension=".svc"/>            
      </buildProviders>
    ...

Doing that now allowed my route with the .svc extension to work. Of course, if I have other .svc services that should continue to work, I’ve pretty much disabled all of them by doing this. However, if those services are in a common subfolder (for example, a folder named services), we may be able to get around this by adding the build provider in a web.config file within that common subfolder.

In any case, I thought the question was interesting as it demonstrated the delicate interplay between routing and build providers.

What others have said

Requesting Gravatar... Seth Sep 07, 2010 5:10 PM
# re: Overriding a .svc Request With Routing
I had a similar approach list week; I was having trouble registering an asset.axd httphandler from web.config. It should be straight forward, but you know, I was using MVC2, Telerik MVC extensions for MVC and all was running on Linux, apache2 with mod_mono.

As much as I think it ____ROCKS____ that all this works, I was prepared to accept the idea that a few minor incompatibilities might exist in the web server configuration area... and I wasn't prepared to switch to bleeding edge versions to see whether it was fixed. So.... I removed the items in web config and instead added the following bit of gunk in my MVC application routing table:


routes.Add(new Route("asset.axd", new RouteAssetHttpHandler()));


with RouteAssetHttpHandler defined as a simple wrapper around the httpHandler


public class RouteAssetHttpHandler : IRouteHandler
{
IHttpHandler IRouteHandler.GetHttpHandler (RequestContext requestContext)
{
return new Telerik.Web.Mvc.WebAssetHttpHandler();
}
}


Worked like a charm!

(no preview, so please don't hit me if the formatting is screwed up)
Requesting Gravatar... Tom Anderson Sep 07, 2010 9:59 PM
# re: Overriding a .svc Request With Routing
Thanks for answering this Phil, really helped me move forward!
Requesting Gravatar... Slava Sep 07, 2010 10:05 PM
# re: Overriding a .svc Request With Routing
<system.webServer>
<rewrite>
<rules>
<rule name="LegacyApiService" stopProcessing="true">
<match url="^Services/api.svc/(.*)$" />
<action type="Rewrite" url="LegacyApi/{R:1}" />
</rule>
</rules>
</rewrite>
</system.webServer>
Requesting Gravatar... Tom Anderson Sep 07, 2010 10:08 PM
# re: Overriding a .svc Request With Routing
@Slava - that approach does work, but this solution is a more robust one that works "as intended".
Requesting Gravatar... Jennifer Sep 08, 2010 1:43 AM
# re: Overriding a .svc Request With Routing
Thanks Phil. This is getting bookmarked for future reference. I have been experimenting with creating applications with just HTML and JavaScript frontends that use AJAX to call web services. I may need this in the future.
Requesting Gravatar... Webdiyer Sep 08, 2010 10:23 AM
# re: Overriding a .svc Request With Routing
I'm eager to know when will the next preview or beta version of asp.net mvc be released? will it support vs intellisense? thanks!
Requesting Gravatar... dandax Sep 08, 2010 11:02 PM
# re: Overriding a .svc Request With Routing
This seems like a lot of work to just remove the .svc extension. Ron Jacobs has a better example on his website (blogs.msdn.com/...). The beauty is it's only 2 lines of code and you don't even need the .svc file! Check out this post on how to remove the file (blogs.msdn.com/...).

Note: I'm not sure this applies to every instance but I've definitely been using it for WCF Data Services.
Requesting Gravatar... Van T Nguyen Sep 12, 2010 11:10 AM
# re: Overriding a .svc Request With Routing
Awesome, I will need it someday :D
Requesting Gravatar... dandax Sep 13, 2010 2:43 AM
# re: Overriding a .svc Request With Routing
My bad... I misunderstood the question. I thought it was a similar issue to one I was working on a few weeks prior.

In any case I can already think of an area where this will come in handy.

Cheers.
Requesting Gravatar... tilovell Dec 20, 2011 6:39 AM
# re: Overriding a .svc Request With Routing
Excuse my newbie question. I have a project where I want .svc files to continue working.

I figured I should be able to remove buildProviders selectively at a directory level, however when I tried with a directory web.config I encountered this:

Parser Error Message: The element 'buildProviders' cannot be defined below the application level.

Source Error:


Line 2: <system.web>
Line 3: <compilation debug="true" targetFramework="4.0">
Line 4: <buildProviders>
Line 5: <remove extension=".svc"/>
Line 6: </buildProviders>


So this doesn't work - any idea why?

What do you have to say?

(will show your gravatar)
Please add 4 and 8 and type the answer here: