ASP.NET Web窗体中的ASMX Web服务路由

注意:此代码中没有MVC。 纯旧Web窗体和.asmx Web服务。

我在我的新公司inheritance了一个大规模的ASP.NET Web Forms&Web Service( .asmx )应用程序。

由于某些需要,我正在尝试为所有Web表单执行URL路由,这是我成功完成的。

现在.asmxroutes.MapPageRoute不起作用。 基于以下文章,我创建了一个IRouteHandler类。 以下是代码的外观:

 using System; using System.Web; using System.Web.Routing; using System.Web.Services.Protocols; using System.Collections.Generic; 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)); } } 

http://mikeoncode.blogspot.in/2014/09/aspnet-web-forms-routing-for-web.html

现在,当我通过Global.asax进行路由时,它适用于根文档文件,但不适用于我的.asmx文件中的Web方法。

  routes.Add("myservice", new System.Web.Routing.Route("service/sDxcdfG3SC", new System.Web.Routing.RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/service/myoriginal.asmx"))); routes.MapPageRoute("", "service/sDxcdfG3SC", "~/service/myoriginal.asmx"); 

目标

我想将.asmx Web方法URL(例如www.website.com/service/myservice.asmx/fetchdata映射到名称中包含模糊名称的URL,如www.website.com/service/ldfdsfsdf/dsd3dfd3d使用.NET路由。

如何才能做到这一点?

使用路由比使用您发布的文章稍微有点棘手,因为您不希望传入的URL具有查询字符串参数,并且看起来WebServiceHandler在没有?op=Method参数的情况下不会调用该方法。

所以,有几个部分:

  1. 自定义路由( ServiceRoute )进行URL重写以添加?op=Method参数
  2. 用于包装调用Web服务的WebServiceHandlerFactoryIRouteHandler
  3. 一组扩展方法,使注册变得容易。

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

用法

您可以使用MapServiceRoute添加一个路径(使用可选名称):

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

或者,您可以调用MapServiceRoutes映射一批Web服务路由:

 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 { { "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" }, }); } } 

注意:如果您在应用程序中使用MVC,通常应这些路径之后注册MVC路由。

参考文献:

  • 使用ASP.NET路由为.asmx Web服务创建路由
  • 用于Web服务的.NET 4 URL路由(asmx)

不是直接的答案,而是值得考虑的事情。

您可以将ASMX服务升级到具有兼容合同的WCF服务,这样您就不必升级客户端了。

有了它,您可以使用已知技术动态路由WCF服务。 由于这种已知技术涉及服务的任意地址,因此您可以将WCF服务绑定到.......foo.asmx端点地址,这样您的客户端不仅不会升级其客户端代理,而且它们也具有完全相同的地址。相同的端点地址。

换句话说,对于客户端,您的动态路由WCF服务看起来与您的旧ASMX服务完全相同。

我们在过去的几年中成功地使用了这种技术,将大多数旧的ASMX升级到WCF,在许多情况下保留了客户端代理。

所有技术细节都记录在我的博客文章中

http://www.wiktorzychla.com/2014/08/dynamic-wcf-routing-and-easy-upgrading.html

您引用的文章不是为asmx WS提供无扩展路由,而是提供从server/whateverYouAre/ws.asmxserver/ws.asmx (真实资源位置)的路由。 这允许JS使用本地路径(当前位置)来调用asmx,而无需担心浏览器所在的位置。

无论如何,也许,只是也许,你可以使用这篇文章作为起点。 我从来没有这样做,所以它只是一个猜测:

消耗WS有两种模式。 如果客户端使用SOAP,请求URL将是:

 /server/service/myoriginal.asmx 

使用SOAPAction http标头和POST主体中的SOAP XML。 您当前的路由解决方案应该可行 但是如果您通过原始HTTP GET / POST(即来自webBrowser)使用WS,则每个webMethod的url为:

 /server/service/myoriginal.asmx/webMethod 

所以我认为你可以提供一些url路由:

 routes.Add("myservice", new System.Web.Routing.Route("service/sDxcdfG3SC/{webMethod}", new System.Web.Routing.RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/service/myoriginal.asmx"))); //Delete routes.MapPageRoute("", "service/sDxcdfG3SC", "~/service/myoriginal.asmx"); from your code, it is wrong even in your actual solution 

并修改GetHttpHandler:

 public IHttpHandler GetHttpHandler(RequestContext requestContext) { return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath + "\" + requestContext.RouteData.Values["webMethod"], requestContext.HttpContext.Server.MapPath(_virtualPath)); } 

/server/service/myoriginal.asmx/webMethod的forms提供所请求资源的原始URL。

我的代码是从头顶写的,所以只需确保_ virtualPath + "/" + requestContext.RouteData.Values["webMethod"]在早期愤怒退出之前创建正确的URL ;-)如果需要修改它。

幸运的是; WebServiceHandlerFactory应该能够找到物理资源,并检查原始URL,按名称执行webMethod。

如果站点在IIS中托管,则可以使用IIS URL Rewrite创建友好URL并根据create -rewrite-rules-for-url-rewrite-module将其重定向到内部路径。 这些规则中的每一个都存储在web.config中,因此可以在开发环境中进行管理

缺点(或取决于您的使用的好处)是原始路径仍然可访问