2015-05-29 2 views
8

У меня есть проект, у которого есть один класс реализации IHttpHandler, который маршрутизирует все запросы с помощью огромного оператора switch и т. Д. Я пытаюсь ввести маршрутизацию атрибутов с помощью ApiControllers, но первый всегда имеет приоритет. Можно ли настроить систему (код или IIS), чтобы веб-ApiControllers имели приоритет над моим единственным классом реализации IHttpHandler? В IIS я сначала помещаю свой AttributeRouting, а затем все aspx, но все же Web Api Controller не обрабатывается первым. Независимо от того, что я делаю (имея их в одном проекте). Я не хочу вводить отдельный проект.Как определить приоритеты Web Api Controllers над IHttpHandler?

Редактировать: Существует IHttpModule, который решает на основе того, что после api/направляет его в определенный файл ashx. Один из них - один из описанных.

Редактировать 2: Подробнее: Если ури не имеет списка фильтрованных объектов [файл, сообщение, свойство ...], он перенаправляется в Resource.aspx

так апи/файл, апи/сообщение, апи/собственность будет обращаться с другими .ashx файлов - в противном случае трафик идет к Resource.ashx ...

в результате запросы, которые имеют API/endpoint1 , api/endpoint2, api/endpoint3 все перейдут в Resource.aspx. Вопрос заключается в том, как перенаправить api/endpoint3 в контроллер API, описанный ниже. Благодаря

упрощенный код архитектуры:

//SolutionName/Api/MyModule.cs (Legacy Code) 
//this routes based on what is after api/ to Resource.ashx or other ashx files 
public class MyModule : IHttpModule { 
    //if url doesn't contain [file,message,property ...] route to Resource.ashx 
} 

//SolutionName/API/Resource.ashx (Legacy Code) 
//this is hit at any request solutionname/api/anything 
public class DefaultHandler : IHttpHandler 
{ 
    public void ProcessRequest(HttpContext context) { 
     String APIBranch = parse(context); 
     switch(APIBranch) 
     { 
      case "endpoint1": methodOne(); break; 
      case "endpoint2": methodTwo(); break; 
      [...] 
      default: throw Exception(); break; 
     } 
    } 
} 

//SolutionName/API/App_Start/AttributeRoutingHttpConfig.cs 
public static class AttributeRoutingHttpConfig 
{ 
    public static void RegisterRoutes(HttpRouteCollection routes) 
    {  
     // See http://github.com/mccalltd/AttributeRouting/wiki for more options. 
     // To debug routes locally using the built in ASP.NET development server, go to /routes.axd 

     routes.MapHttpAttributeRoutes(); 
    } 

    public static void Start() 
    { 
     RegisterRoutes(GlobalConfiguration.Configuration.Routes); 
    } 
} 

//SolutionName/API/Controllers/MyController.cs 
//this should have been hit for a GET on solutionname/api/endpoint3/id 
[RoutePrefix("endpoint3")] 
public class MyController : ApiController 
{ 
    private IModelDao modelDao; 

    MyController(IModelDao modelDao){ 
     this.modelDao = modelDao; 
    } 

    [Route("{id}")] 
    [HttpGet] 
    public Model GetSomething(int id) 
    { 
     Model model = modelDao.GetSomething(id); 
     return model; 
    } 
} 
+1

'HttpHandlers' работы до того, как контроллер апи ударил. Обработчики используются для проверки и потенциального перехвата или перенаправления («действовать») на заданный запрос ресурса. Это не работает наоборот. Вам придется взять «default» из инструкции переключателя обработчика, чтобы заставить это работать. Однако вы можете сделать то же самое с веб-api - вы можете явно указать все доступные маршруты в конфигурации и иметь маршрут по умолчанию, который переносит их на маршрут, который возвращает ошибку. – ps2goat

+1

Теперь, когда я немного подумал об этом, вы сможете проверить настроенные маршруты api и пропустить их. Теперь я хочу попробовать ... – ps2goat

+0

@ ps2goat Могу ли я иметь HttpModule, который вместо этого ударил бы моего ApiController? Хотя я не знаю, как это будет. Я надеюсь, что это не будет иметь производительности. –

ответ

2

я нашел два решения этой проблемы. Первый заключается в том, чтобы модифицировать модуль, который перезаписывает URL-адреса, вставив флажок, если система маршрутизации Web API может обрабатывать запрос. Второй - добавить еще один модуль в приложение, который будет направлять запросы на обработчик веб-API с помощью HttpContext.RemapHandler().

Вот код:

Первое решение.

Если ваш модуль выглядит следующим образом:

public class MyModule: IHttpModule 
{ 
    public void Dispose(){} 

    public void Init(HttpApplication context) 
    { 
     context.BeginRequest += (object Sender, EventArgs e) => 
     { 
      HttpContext httpContext = HttpContext.Current; 
      string currentUrl = httpContext.Request.Url.LocalPath.ToLower(); 
      if (currentUrl.StartsWith("/api/endpoint0") || 
       currentUrl.StartsWith("/api/endpoint1") || 
       currentUrl.StartsWith("/api/endpoint2")) 
      { 
       httpContext.RewritePath("/api/resource.ashx"); 
      } 
     }; 
    } 
} 

Затем вам нужно изменить его так:

public void Init(HttpApplication context) 
{ 
    context.BeginRequest += (object Sender, EventArgs e) => 
    { 
     HttpContext httpContext = HttpContext.Current; 
     var httpRequestMessage = new HttpRequestMessage(
      new HttpMethod(httpContext.Request.HttpMethod), 
      httpContext.Request.Url); 
     IHttpRouteData httpRouteData = 
      GlobalConfiguration.Configuration.Routes.GetRouteData(httpRequestMessage); 
     if (httpRouteData != null) //enough if WebApiConfig.Register is empty 
      return; 

     string currentUrl = httpContext.Request.Url.LocalPath.ToLower(); 
     if (currentUrl.StartsWith("/api/endpoint0") || 
      currentUrl.StartsWith("/api/endpoint1") || 
      currentUrl.StartsWith("/api/endpoint2")) 
     { 
      httpContext.RewritePath("/api/resource.ashx"); 
     } 
    }; 
} 

Второе решение.

Модуль переназначения обработчиков:

public class RemappingModule: IHttpModule 
{ 
    public void Dispose() { } 

    public void Init(HttpApplication context) 
    { 
     context.PostResolveRequestCache += (src, args) => 
     { 
      HttpContext httpContext = HttpContext.Current; 
      string currentUrl = httpContext.Request.FilePath; 
      if (!string.IsNullOrEmpty(httpContext.Request.QueryString.ToString())) 
       currentUrl += "?" + httpContext.Request.QueryString; 
      //checking if url was rewritten 
      if (httpContext.Request.RawUrl != currentUrl) 
      { 
       //getting original url 
       string url = string.Format("{0}://{1}{2}", 
        httpContext.Request.Url.Scheme, 
        httpContext.Request.Url.Authority, 
        httpContext.Request.RawUrl); 
       var httpRequestMessage = new HttpRequestMessage(
        new HttpMethod(httpContext.Request.HttpMethod), url); 
       //checking if Web API routing system can find route for specified url 
       IHttpRouteData httpRouteData = 
        GlobalConfiguration.Configuration.Routes.GetRouteData(httpRequestMessage); 
       if (httpRouteData != null) 
       { 
        //to be honest, I found out by experiments, that 
        //context route data should be filled that way 
        var routeData = httpContext.Request.RequestContext.RouteData; 
        foreach (var value in httpRouteData.Values) 
         routeData.Values.Add(value.Key, value.Value); 
        //rewriting back url 
        httpContext.RewritePath(httpContext.Request.RawUrl); 
        //remapping to Web API handler 
        httpContext.RemapHandler(
         new HttpControllerHandler(httpContext.Request.RequestContext.RouteData)); 
       } 
      } 
     }; 
    } 
} 

Эти решения работают, когда метод WebApiConfig.Register пуст, но если были маршруты с шаблонами как "api/{controller}" то любой путь с двумя сегментами, начиная с «апи» пройдет проверку , даже если нет контроллеров с указанным именем, и ваш модуль может сделать что-то userfull для этого пути.В этом случае вы можете, например, использовать метод от this, чтобы проверить, существует ли контроллер.

Также система маршрутизации веб-API будет принимать маршрут, даже если найденный контроллер не обрабатывает запросы на текущий метод http. Вы можете использовать потомок RouteFactoryAttribute и HttpMethodConstraint, чтобы избежать этого.

UPD Испытано на этом контроллеры:

[RoutePrefix("api/endpoint1")] 
public class DefaultController : ApiController 
{ 
    [Route("{value:int}")] 
    public string Get(int value) 
    { 
     return "TestController.Get: value=" + value; 
    } 
} 

[RoutePrefix("api/endpoint2")] 
public class Endpoint2Controller : ApiController 
{ 
    [Route("segment/segment")] 
    public string Post() 
    { 
     return "Endpoint2:Post"; 
    } 
} 
+0

Спасибо за ваши усилия! Я думаю, что вы немного упустили этот момент - но я подробно прочитаю ответ позже. У меня есть 3 модуля, один из которых перенаправляет на/api /, и он обрабатывает всю ситуацию, которую он поддерживает через Resourse.aspx (HttpHandler) ... Но у меня есть 3 контроллера веб-api, которые я хочу, чтобы они обрабатывали трафик до того, как он попал в Resource.aspx. В вашем коде все эти конечные точки1,2,3 вы направляете в Resource.aspx, который уже происходит, и я не хочу это .. –

+0

@MichailMichailidis Я до сих пор не понимаю. Вы можете либо предотвратить перенаправление на/api/в своем модуле, либо отменить это перенаправление до того, как HttpHandler в Resource.aspx будет выполнен. Разве это не то, что вам нужно? – Valyok26

+0

Я отредактировал вопрос, чтобы дать больше ясности - и я добавил комментарий в httpmodule .. дайте мне знать, если это еще не ясно - спасибо - проблема в том, что любая переадресация на/api/... снова ударит модули и процесс начинается снова. –