如何通过IHttpHandler优先处理Web Api控制器?

Mic*_*dis 8 c# asp.net asp.net-mvc httphandler asp.net-web-api

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

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

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

所以api/file,api/message,api/property将从其他.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
 }
Run Code Online (Sandbox Code Playgroud)
//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;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)
//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);
    }
}
Run Code Online (Sandbox Code Playgroud)
//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;
    }
}
Run Code Online (Sandbox Code Playgroud)

Val*_*k26 2

我找到了解决这个问题的两种方法。第一个是通过插入检查 Web API 路由系统是否可以处理请求来修改重写 url 的模块。第二个是向应用程序添加另一个模块,该模块将使用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");
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你需要像这样改变它:

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");
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

第二个解决方案。

重新映射处理程序的模块:

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));
                }
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

这些解决方案在方法WebApiConfig.Register为空时有效,但如果存在带有模板的路由,那么"api/{controller}"任何具有以“api”开头的两个段的路径都会通过检查,即使没有具有指定名称的控制器,并且您的模块可以执行一些有用的操作这条路。例如,在这种情况下,您可以使用答案中的方法来检查控制器是否存在。

即使发现控制器不处理当前 http 方法的请求,Web API 路由系统也会接受路由。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";
    }
}
Run Code Online (Sandbox Code Playgroud)