Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping to an ASMX service using routing in ASP.NET MVC

I wonder if there's any way to map a URL to ASMX service much like it is done with pages (using routes.MapPageRoute() method).

When I tried to do it simply by pointing the MapPageRoute to my service I get the error

Type 'MvcApplication1.Services.EchoService' does not inherit from 'System.Web.UI.Page'.

Matthias.

like image 421
Matthias Hryniszak Avatar asked May 04 '10 11:05

Matthias Hryniszak


2 Answers

I stumbled upon this question trying to find the answer myself, and since I did figure out a way to do it, I figured I'd answer it.

The reason I needed this is because I'm converting an old ASP.NET website to ASP.NET MVC, and for compatibility purposes I need a web service available at a specific URL. However, the path of that URL is now handled by a Controller in the new site, so I cannot have a physical directory with the same name (since that will prevent the controller from being invoked for other URLs with that path other than the web service).

The PageRouteHandler, which is used by RouteCollection.MapPageRoute, indeed requires that the handler for the target path derives from System.Web.Page, which isn't the case for web services. So instead, it is necessary to create a custom page handler:

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 will create an appropriate handler for the web service based on the request and mapped virtual path of the service.

You can add a route for a web service now as follows:

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

Note: you must specify the controller and action values in the route value dictionary (even though they're set to null), otherwise the Html.ActionLink helper will always use this route for every single link (unless a match was found in the list before this route). Since you probably want to add this route before the default MVC route, it's important that it doesn't get matched that way.

Of course, you can create your own extension method to alleviate this task:

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

After which you can simply do:

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

I hope this helps someone, despite the age of this question. :)

like image 131
Sven Avatar answered Sep 21 '22 08:09

Sven


Now that we waited two years with an anwer, how about using Web API instead? :)

EDIT: Kidding aside if that doesn't work for you and you still need an answer, leave a comment and I will see if I can't come up with a better one.

like image 21
Mirko Avatar answered Sep 19 '22 08:09

Mirko