2015-05-29 14 views
8

我有一个遗留项目,有一个单一的IHttpHandler实现类,使用巨大的switch语句等路由所有请求。我试图引入属性路由与ApiControllers,但第一个始终具有优先级。是否可以配置系统(代码或IIS),以便Web ApiController优先于我的单个IHttpHandler实现类?在IIS中,我首先将我的AttributeRouting放入,然后是所有的aspx,但仍然没有先处理Web Api Controller ..不管我做什么(在同一个项目下都有)。我不想介绍一个单独的项目。如何优先使用IHabpHandler的Web Api控制器?

编辑:有一个IHttpModule根据api后的内容/将其路由到特定的ashx文件。其中之一是描述的..

编辑2:更具体地说:如果uri没有过滤的东西[文件,消息,属性...]的列表,它被路由到Resource.aspx

所以API /文件,API /消息,API /属性会从其他.ashx的文件处理 - 否则交通去Resource.ashx ...

结果有API /端点1请求,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'的API控制器被击中之前的工作。处理程序用于检查和潜在拦截或重定向('采取行动')给定的资源请求。反过来也不行。你必须从处理程序的switch语句中取出'default'才能使其工作。但是,您可以使用web api执行相同的操作 - 您可以明确指定配置中的所有可用路由,并使用默认路由将它们引导至返回错误的路由。 – ps2goat

+1

现在我已经想了一下,你可能会检查配置的API路线并让它们通过。现在我想尝试它... – ps2goat

+0

@ ps2goat我可以有一个HttpModule,它会打我的ApiController,而不是 - 虽然我不知道这将如何..我希望这不会有性能打击.. –

回答

2

我已经发现了两个解决问题的对策。首先是通过插入检查Web API路由系统是否可以处理请求来修改重写URL的模块。第二种是向应用程序添加另一个模块,该模块将使用HttpContext.RemapHandler()将请求发送到Web API处理程序。

这里是代码:

首选解决方案。

如果你的模块看起来是这样的:

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}"然后用两段任何路径开始与“API”将通过检查,即使没有指定名称的控制器,并且您的模块可以为此路径执行某些用户操作。在这种情况下,例如,您可以使用this答案中的方法来检查控制器是否存在。

即使发现控制器不处理当前http方法的请求,Web API路由系统也会接受路由。您可以使用RouteFactoryAttributeHttpMethodConstraint的后代来避免这种情况。

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个web api控制器,我希望他们在点击Resource.aspx之前处理流量。在您的代码中,所有这些端点1,2,3都将指向已经发生的Resource.aspx,并且我不想要它.. –

+0

@MichailMichailidis我还是不明白。您可以阻止重定向到模块中的/ api /,或者在执行Resource.aspx中的HttpHandler之前回滚此重定向。这不是你需要的吗? – Valyok26

+0

我编辑了这个问题,以提供更多的清晰度 - 并且我在httpmodule中添加了一条评论。让我知道它是否还不清楚 - 谢谢 - 问题是任何重定向到/ api/...都会再次击中模块和过程重新开始.. –