Сопоставление со службой ASMX с использованием маршрутизации в ASP.NET MVC - PullRequest
5 голосов
/ 04 мая 2010

Интересно, есть ли способ сопоставить URL-адрес со службой ASMX так же, как это делается со страницами (с использованием метода routes.MapPageRoute()).

Когда я пытался сделать это, просто указав MapPageRoute на мой сервис, я получаю ошибку

Тип 'MvcApplication1.Services.EchoService' не наследуется от 'System.Web.UI.Page'.

Matthias.

Ответы [ 3 ]

5 голосов
/ 30 мая 2013

Я наткнулся на этот вопрос, пытаясь найти ответ сам, и так как я нашел способ сделать это, я решил ответить на него.

Причина, по которой я нуждался в этом, заключается в том, что я преобразовываю старый веб-сайт ASP.NET в ASP.NET MVC, и для целей совместимости мне нужен веб-сервис, доступный по определенному URL-адресу. Однако путь к этому URL-адресу теперь обрабатывается контроллером на новом сайте, поэтому я не могу иметь физический каталог с тем же именем (так как это предотвратит вызов контроллера для других URL-адресов с этим путем, отличным от веб-службы). ).

PageRouteHandler, который используется RouteCollection.MapPageRoute, действительно требует, чтобы обработчик целевого пути был производным от System.Web.Page, что не относится к веб-службам. Поэтому вместо этого необходимо создать собственный обработчик страницы:

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

Этот обработчик маршрута создаст соответствующий обработчик для веб-службы на основе запроса и сопоставленного виртуального пути службы.

Теперь вы можете добавить маршрут для веб-службы следующим образом:

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

Надеюсь, это кому-нибудь поможет, несмотря на возраст этого вопроса. :)

1 голос
/ 17 мая 2012

Теперь, когда мы два года ждали с ответом, как насчет использования Web API вместо этого? :)

РЕДАКТИРОВАТЬ: шучу в сторону, если это не работает для вас, и вам все еще нужен ответ, оставьте комментарий, и я посмотрю, не смогу ли я найти лучший.

0 голосов
/ 04 августа 2015

Я пытался решить исходное сообщение ( также размещено здесь ), но я столкнулся с серьезной проблемой. Я не могу настроить таргетинг на веб-метод в веб-сервисе. При попытке сделать это я получил исключение о том, что файл не существует.

Если вы действительно хотите сопоставить маршрут MVC с веб-службой .ASMX, решение объясняется здесь .

Я полагаю, что это решение является хакерским путем злоупотребления встроенными типами, поскольку оно использует отражение для обхода преднамеренных элементов ограниченного доступа для встроенных типов .NET.

Вот метод, который я использую, который я считаю гораздо более простым.

  1. Прежде всего, вы должны создать свои веб-сервисы в файле .ASMX, чтобы все веб-сервисы действовали как опубликованные интерфейсы. На этом этапе нам не нужно напрямую ориентироваться на методы веб-службы .ASMX. Важный код был повторно использован в базовых классах, которые не зависят от точки входа приложения. В любом случае нам это нужно, чтобы мы могли запускать автоматизированные тесты!

  2. Замените метод веб-службы MVC новым маршрутом с пользовательским обработчиком маршрута и обработчиком 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". Это потому, что я хочу улучшить класс контроллера гигант и создать обработчик для каждого действия, чтобы у меня был небольшой класс с единственной ответственностью за каждый веб-метод.

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

Из метода ProcessRequest IHttpHandler мы получаем полный контроль над действиями этого маршрута. При хорошо спроектированной архитектуре веб-сервисов все, что нам нужно сделать, - это вызвать классы, которые поддерживают веб-метод .ASMX, к которому вы пытаетесь сопоставить маршрут.

В результате получается очень чистый файл Global.asax. Мы могли бы сделать все это без маршрутизации URL-адреса, просто проверив URL-адрес вручную, но это слишком важно для файла, чтобы раздуться.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...