ASP.NET Web窗体中的ASMX Web服务路由
注意:此代码中没有MVC。 纯旧Web窗体和
.asmx
Web服务。
我在我的新公司inheritance了一个大规模的ASP.NET Web Forms&Web Service( .asmx
)应用程序。
由于某些需要,我正在尝试为所有Web表单执行URL路由,这是我成功完成的。
现在.asmx
, routes.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
参数的情况下不会调用该方法。
所以,有几个部分:
- 自定义路由(
ServiceRoute
)进行URL重写以添加?op=Method
参数 - 用于包装调用Web服务的
WebServiceHandlerFactory
的IRouteHandler
。 - 一组扩展方法,使注册变得容易。
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.asmx
到server/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中,因此可以在开发环境中进行管理
缺点(或取决于您的使用的好处)是原始路径仍然可访问