【问题标题】:Mapping to an ASMX service using routing in ASP.NET MVC在 ASP.NET MVC 中使用路由映射到 ASMX 服务
【发布时间】:2010-05-04 11:40:58
【问题描述】:

我想知道是否有任何方法可以将 URL 映射到 ASMX 服务,就像处理页面一样(使用 routes.MapPageRoute() 方法)。

当我尝试简单地将 MapPageRoute 指向我的服务时,我得到了错误

类型“MvcApplication1.Services.EchoService”不继承自“System.Web.UI.Page”。

马蒂亚斯。

【问题讨论】:

    标签: c# asp.net .net asp.net-mvc


    【解决方案1】:

    我偶然发现了这个问题,试图自己找到答案,因为我确实找到了解决方法,所以我想我会回答它。

    我需要这个的原因是因为我正在将一个旧的 ASP.NET 网站转换为 ASP.NET MVC,并且出于兼容性目的,我需要一个在特定 URL 上可用的 Web 服务。但是,该 URL 的路径现在由新站点中的控制器处理,因此我不能有一个具有相同名称的物理目录(因为这将阻止控制器为具有该路径的其他 URL 调用,而不是 Web 服务)。

    RouteCollection.MapPageRoute 使用的PageRouteHandler 确实要求目标路径的处理程序源自System.Web.Page,而Web 服务则不是这种情况。因此,有必要创建一个自定义页面处理程序:

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

    此路由处理程序将根据请求和映射的服务虚拟路径为 Web 服务创建适当的处理程序。

    您现在可以为 Web 服务添加路由,如下所示:

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

    注意:您必须在路由值字典中指定控制器和操作值(即使它们设置为 null),否则 Html.ActionLink 助手将始终为每个链接使用此路由(除非找到匹配项在此路线之前的列表中)。由于您可能希望在默认 MVC 路由之前添加此路由,因此不要以这种方式匹配它,这一点很重要。

    当然,您可以创建自己的扩展方法来减轻此任务:

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

    之后你可以简单地做:

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

    我希望这对某人有所帮助,尽管这个问题已经很老了。 :)

    【讨论】:

      【解决方案2】:

      既然我们已经等待了两年的答案,那么改用 Web API 怎么样? :)

      编辑:如果这对您不起作用并且您仍然需要答案,请在一边开玩笑,请发表评论,我会看看我是否能想出一个更好的答案。

      【讨论】:

        【解决方案3】:

        我尝试了原始帖子的解决方案 (also posted here),但遇到了一个严重的问题。我无法在 Web 服务中定位 Web 方法。尝试这样做时,我收到一个异常,指出该文件不存在。

        如果您真的想将 MVC 路由映射到 .ASMX Web 服务,则解释了解决方案 here

        我认为滥用内置类型的解决方案是一种黑客攻击,因为它使用反射绕过内置 .NET 类型上的有意限制访问成员。

        这是我正在采用的方法,我认为它更简单。

        1. 首先,您应该在 .ASMX 文件中设计您的 Web 服务,以便所有 Web 服务都充当已发布的接口。此时,我们不需要直接针对 .ASMX Web 服务的方法。重要的代码已在与应用程序入口点无关的核心类中可重用。无论如何我们都需要这个,以便我们可以运行自动化测试!

        2. 将 MVC 的 Web 服务方法替换为具有自定义路由处理程序和 http 处理程序的新路由。

        旧路线:

                routes.MapRoute(
                    "Lead",
                    "lead/{action}.mvc",
                    new { controller = "Lead" });
        

        新路线:

                var dict = new RouteValueDictionary
                           {
                               { "controller", null },
                               { "action", null }
                           };
                var handler = new LeadRouteHandler();
                var route = new Route("lead/MVC_General.mvc", dict, handler);
                routes.Add("Lead", route);
        

        请注意,新路由的动作硬编码为“MVC_General”。这是因为我希望改进 giant 控制器类并为每个操作创建一个处理程序,这样我就可以拥有一个对每个 Web 方法负责的小类。

        1. 实现路由的处理程序。

        IRouteHandler:

        public class LeadRouteHandler : IRouteHandler
        {
            public IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new LeadHttpHandler();
            }
        }
        

        IHttpHandler:

        public class LeadHttpHandler : IHttpHandler
        {
        
            public bool IsReusable
            {
                get { return false; }
            }
        
            public void ProcessRequest(HttpContext context)
            {
                // Just enough code to preserve the route's json interface for tests
                var typedResult = new PsaLeadSubmissionResult();
                typedResult.Registered = false;
                typedResult.Message = new List<string>
                                      {
                                          "Not Implemented"
                                      };
        
                var jsonResult = JsonConvert.SerializeObject(typedResult);
        
                context.Response.Write(jsonResult);
            }
        }
        

        从 IHttpHandler 的 ProcessRequest 方法中,我们可以完全控制该路由的操作。有了精心设计的 Web 服务架构,我们所需要做的就是调用支持您尝试将路由映射到的 .ASMX Web 方法的类。

        结果是一个非常干净的 Global.asax 文件。我们可以在没有 URL 路由的情况下通过手动检查 URL 来完成所有这些操作,但这对于一个文件来说太重要了以至于不能膨胀。

        【讨论】:

          猜你喜欢
          • 2010-09-05
          • 1970-01-01
          • 2012-09-03
          • 2011-04-10
          • 2010-11-08
          • 2019-03-29
          • 2016-12-13
          • 1970-01-01
          • 2010-11-04
          相关资源
          最近更新 更多