[英]How to prioritize Web Api Controllers over IHttpHandler?
我有一個舊項目,該項目只有一個IHttpHandler實現類,該類使用巨大的switch語句等路由所有請求。我試圖引入ApiControllers進行屬性路由,但第一個始終具有優先級。 是否可以配置系統(代碼或IIS),以使Web ApiControllers優先於我的單個IHttpHandler實現類? 在IIS中,我先放我的AttributeRouting,然后放所有的aspx,但無論如何我都不會首先處理Web Api Controller(將它們放在同一個項目下)。 我不想介紹一個單獨的項目。
編輯:有一個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
}
//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;
}
}
我找到了解決此問題的兩種解決方案。 首先是修改模塊,該模塊通過插入檢查Web API路由系統是否可以處理請求來重寫url。 第二個方法是向應用程序添加另一個模塊,該模塊將使用HttpContext.RemapHandler()
將請求定向到Web API Handler。
這是代碼:
第一個解決方案。
如果您的模塊如下所示:
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”開頭的兩段路徑都將通過檢查,即使沒有控制器指定的名稱,並且您的模塊可以對此路徑執行一些userfull的操作。 例如,在這種情況下,您可以使用此答案中的方法來檢查控制器是否存在。
即使找到的控制器不處理當前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";
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.