我有一个遗留项目,它有一个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
}
//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;
}
}
答案 0 :(得分: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}"
等模板的路径,那么任何路径都有两个以&#34开头的段; api"会通过检查,即使没有指定名称的控制器,并且您的模块可以为此路径做一些用户满意的操作。在这种情况下,您可以使用this answer中的方法来检查控制器是否存在。
即使找到的控制器不处理当前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";
}
}