Overriding a .svc Request With Routing

asp.net, asp.net mvc, code 0 comments suggest edit

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.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

11 responses

  1. Avatar for Seth
    Seth September 7th, 2010

    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)

  2. Avatar for Tom Anderson
    Tom Anderson September 7th, 2010

    Thanks for answering this Phil, really helped me move forward!

  3. Avatar for Slava
    Slava September 7th, 2010

    <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>

  4. Avatar for Tom Anderson
    Tom Anderson September 7th, 2010

    @Slava - that approach does work, but this solution is a more robust one that works "as intended".

  5. Avatar for Jennifer
    Jennifer September 7th, 2010

    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.

  6. Avatar for Webdiyer
    Webdiyer September 8th, 2010

    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!

  7. Avatar for dandax
    dandax September 8th, 2010

    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.

  8. Avatar for Van T Nguyen
    Van T Nguyen September 12th, 2010

    Awesome, I will need it someday :D

  9. Avatar for dandax
    dandax September 12th, 2010

    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.

  10. Avatar for tilovell
    tilovell December 20th, 2011

    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?

  11. Avatar for Vinothkumar.R
    Vinothkumar.R May 28th, 2013

    After adding it, I got below error, any sol?

    There is no build provider registered for the extension

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.Web.HttpException: There is no build provider registered for the extension '.svc'. You can register one in the <compilation><buildproviders> section in machine.config or web.config. Make sure is has a BuildProviderAppliesToAttribute attribute which includes the value 'Web' or 'All'.