It is slightly more tricky to do this with routing than in the article you posted because you don't want the incoming URL to have a query string parameter and it looks like the WebServiceHandler
won't call the method without an ?op=Method
parameter.
So, there are a few parts to this:
- A custom route (
ServiceRoute
) to do URL rewriting to add the ?op=Method
parameter
- An
IRouteHandler
to wrap the WebServiceHandlerFactory
that calls the web service.
- A set of extension methods to make registration easy.
ServiceRoute
public class ServiceRoute : Route
{
public ServiceRoute(string url, string virtualPath, RouteValueDictionary defaults, RouteValueDictionary constraints)
: base(url, defaults, constraints, new ServiceRouteHandler(virtualPath))
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// Run a test to see if the URL and constraints don't match
// (will be null) and reject the request if they don't.
if (base.GetRouteData(httpContext) == null)
return null;
// Use URL rewriting to fake the query string for the ASMX
httpContext.RewritePath(this.VirtualPath);
return base.GetRouteData(httpContext);
}
}
ServiceHandler
public class ServiceRouteHandler : IRouteHandler
{
private readonly string virtualPath;
private readonly WebServiceHandlerFactory handlerFactory = new WebServiceHandlerFactory();
public ServiceRouteHandler(string virtualPath)
{
if (virtualPath == null)
throw new ArgumentNullException(nameof(virtualPath));
if (!virtualPath.StartsWith("~/"))
throw new ArgumentException("Virtual path must start with ~/", "virtualPath");
this.virtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Strip the query string (if any) off of the file path
string filePath = virtualPath;
int qIndex = filePath.IndexOf('?');
if (qIndex >= 0)
filePath = filePath.Substring(0, qIndex);
// Note: can't pass requestContext.HttpContext as the first
// parameter because that's type HttpContextBase, while
// GetHandler expects HttpContext.
return handlerFactory.GetHandler(
HttpContext.Current,
requestContext.HttpContext.Request.HttpMethod,
virtualPath,
requestContext.HttpContext.Server.MapPath(filePath));
}
}
RouteCollectionExtensions
public static class RouteCollectionExtensions
{
public static void MapServiceRoutes(
this RouteCollection routes,
Dictionary<string, string> urlToVirtualPathMap,
object defaults = null,
object constraints = null)
{
foreach (var kvp in urlToVirtualPathMap)
MapServiceRoute(routes, null, kvp.Key, kvp.Value, defaults, constraints);
}
public static Route MapServiceRoute(
this RouteCollection routes,
string url,
string virtualPath,
object defaults = null,
object constraints = null)
{
return MapServiceRoute(routes, null, url, virtualPath, defaults, constraints);
}
public static Route MapServiceRoute(
this RouteCollection routes,
string routeName,
string url,
string virtualPath,
object defaults = null,
object constraints = null)
{
if (routes == null)
throw new ArgumentNullException("routes");
Route route = new ServiceRoute(
url: url,
virtualPath: virtualPath,
defaults: new RouteValueDictionary(defaults) { { "controller", null }, { "action", null } },
constraints: new RouteValueDictionary(constraints)
);
routes.Add(routeName, route);
return route;
}
}
Usage
You can either use MapServiceRoute
to add the routes one at a time (with an optional name):
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
routes.MapServiceRoute("AddRoute", "service/ldfdsfsdf/dsd3dfd3d", "~/service/myoriginal.asmx?op=Add");
routes.MapServiceRoute("SubtractRoute", "service/ldfdsfsdf/dsd3dfd3g", "~/service/myoriginal.asmx?op=Subtract");
routes.MapServiceRoute("MultiplyRoute", "service/ldfdsfsdf/dsd3dfd3k", "~/service/myoriginal.asmx?op=Multiply");
routes.MapServiceRoute("DivideRoute", "service/ldfdsfsdf/dsd3dfd3v", "~/service/myoriginal.asmx?op=Divide");
}
}
Alternatively, you can call MapServiceRoutes
to map a batch of your web service routes at once:
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
routes.MapServiceRoutes(new Dictionary<string, string>
{
{ "service/ldfdsfsdf/dsd3dfd3d", "~/service/myoriginal.asmx?op=Add" },
{ "service/ldfdsfsdf/dsd3dfd3g", "~/service/myoriginal.asmx?op=Subtract" },
{ "service/ldfdsfsdf/dsd3dfd3k", "~/service/myoriginal.asmx?op=Multiply" },
{ "service/ldfdsfsdf/dsd3dfd3v", "~/service/myoriginal.asmx?op=Divide" },
});
}
}
NOTE: If you were to have MVC in the application, you should generally register your MVC routes after these routes.
References: