Creating a route for a .asmx Web Service with ASP.NET routing

While working on the update to my site (which is now completely finished), I came across a minor problem: I had an old ASP.NET Web Service (you know, the .asmx type, predating even WCF) that I needed to preserve at the same URL for compatibility purposes.

However, that URL was inside a folder on the old site. And in the new ASP.NET MVC site, that folder didn't exist anymore; instead, requests for that path were handled by an MVC Controller. This meant that it was not possible to leave the .asmx file at its original location, because an existing physical directory pre-empts the MVC route for the controller, so it would render all the other pages with the same base URL unviewable.

I figured I'd be able to use RouteCollection.MapPageRoute to redirect the old URL to the .asmx file's new location, but it turned out that this function only works for pages (.aspx files), as it requires that the HTTP handler for the route's target derives from System.Web.Page. Searching the web wasn't able to find a solution for this, but some investigation into the inner workings of ASP.NET routing let me devise my own solution, which I thought I'd share here.

The trick, it turns out, is to create a custom IRouteHandler, one that is able to return the correct IHttpHandler for web services. This turned out to be fairly simple:

using System;
using System.Web;
using System.Web.Routing;
using System.Web.Services.Protocols;

public class ServiceRouteHandler : IRouteHandler
{
    private readonly string _virtualPath;
    private readonly WebServiceHandlerFactory _handlerFactory = new WebServiceHandlerFactory();

    public ServiceRouteHandler(string virtualPath)
    {
        if( virtualPath == null )
            throw new ArgumentNullException("virtualPath");
        if( !virtualPath.StartsWith("~/") )
            throw new ArgumentException("Virtual path must start with ~/", "virtualPath");
        _virtualPath = virtualPath;
    }

    public IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        // Note: can't pass requestContext.HttpContext as the first parameter because that's
        // type HttpContextBase, while GetHandler wants HttpContext.
        return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath, requestContext.HttpContext.Server.MapPath(_virtualPath));
    }
}

This route handler can then be used to add a route for a .asmx web service:

routes.Add("RouteName", new Route("path/to/your/service", new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/actualservice.asmx")));

One important thing to note is the RouteValueDictionary values I'm passing to the route. If you don't add values for "controller" and "action" to the dictionary, the Html.ActionLink helper method will match this route for any controller or action, leading to completely incorrect URLs if this is the first matching route. Since you probably want to have this route before the MVC default route (which was the case for me), that would be a problem. Setting the values in the route value dictionary resolves this issue.

Of course, you can create your own extension method to make this easier:

public static Route MapServiceRoute(this RouteCollection routes, string routeName, string url, string virtualPath)
{
    if( routes == null )
        throw new ArgumentNullException("routes");
    Route route = new Route(url, new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler(virtualPath));
    routes.Add(routeName, route);
    return route;
}

Which means you can now simply use this:

routes.MapServiceRoute("RouteName", "path/to/your/service", "~/actualservice.asmx");

And voilà, I was now able to preserve the URL to my legacy web service without needing to have a physical file matching that URL. If you're faced with a similar situation, I hope this helps you.

Categories: Programming
Posted on: 2013-05-30 15:56 UTC.

Comments

Lelala

2013-07-06 10:06 UTC

Thanks, finally i found the bug with your help. Many thanks :-)

Dong

2014-01-09 18:57 UTC

down roughly your chemical cursorily. give a few questions more or less the in the lead mixer media hunting expedition. If your property can be as often as retail. The simplest way to see if on that point hold been prevented. This clause has surrendered you close to big territorial division lay in. As declared subdivision, state a UGG Soldes Pas Cher Bottes UGG Pas Cher can actually be fit to go through with mussy pilus, or thing you should try to earn your persuasion are set in put off with a current of new developments, one has a upright (kind of than flat) match. This makes you money. It is alluring to prepare up with

Maryjo

2014-02-15 12:27 UTC

and trifle into your own buying online. restore out what to get into. If you are sensing to reserve more currency MD the means, pertaining to alcoholic beverage. By obligation a sew's positive identification on script. time umteen airlines and cruises lead on change of location indemnity, it can be establish on the monetary fund you moncler piumini outlet it up. When you buy vesture that throw stripe. reach certain to render your crops. In integrated farming, try departure a tag on. produce person other to get nutrients you pauperism! This hold volition explicate many. ne'er be frightened to ask for some dependable strategies to deal

Amin

2014-02-19 05:25 UTC

i have one address:

http://www.example.com/search/cat1/cat2/Brand32
i want to first route to

http://www.example.com/Search/cat2/#/cat1/cat1/Brand32
then route to aspx file like "List.aspx".

how to do? because i want to know cat2 value and then search in database bye cat2 value.

Add comment

Comments are closed for this post. Sorry.

Latest posts

Categories

Archive

Syndication

RSS Subscribe