我们有一个使用 ASP.Net URL Rewrite 2.0 的 Web 窗体应用程序。以下重写规则配置为允许访问某些页面而不指定 .ashx扩展名:
<rule name="RewriteASHX">
<match url="(customers|orders|products.*)"/>
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true"/>
</conditions>
<action type="Rewrite" url="{R:1}.ashx"/>
</rule>
使用 Web API,我们希望逐渐用 Web API Controller 替换基于 Web 窗体的 API 页面。安装 URL Rewrite 后,只有符合上述规则的 Controller 才会正确路由到 Web API Controller 。 (将排除项添加到 URL 重写规则似乎根本不会让请求到达 Controller ,从而给出 404 结果。)
Web API Controller 的设置方式如下:
public class CustomersController : ApiController
{
[Route("api/v2/customers")]
[ResponseType(typeof(CustomerCollection))]
public IHttpActionResult Get()
{
...
}
[Route("api/v2/customers/{identifier}")]
[ResponseType(typeof(Customer))]
public IHttpActionResult Get(string identifier)
{
...
}
}
如上所述,我们只能让 Web API 请求在符合 URL 重写条件的情况下进行路由。
The parameterless
api/v2/customersinCustomerControllerworks because it matches<match url="(customers|orders|products.*)"/>, butapi/v2/customers/C123fails because it does not match the rewrite condition.
配置 URL 重写以允许 Web API 请求正确路由的最佳方法是什么?
请您参考如下方法:
URL Rewrite 模块基本上有两个不同的功能:
- 重写传入的 URL 以使其“看起来”像网络服务器的另一个 URL
- 将传入的 URL 重定向到另一个 URL
自从引入了 .NET 路由(用于 ASP.NET、ASP.NET MVC 和 Web API)后,URL 重写在很大程度上是不必要的。路由要好得多。通过路由,传入请求被准确地发送到目标 Controller 方法。通过重写,HTTP 上下文被重写以欺骗 ASP.NET 将请求发送到其他地方,这可能会导致依赖 HTTP 上下文值工作的事物出现问题。
另一方面,重定向对于 URL 重写仍然非常有用。尤其是涉及到您正在升级的旧应用程序的遗留 URL 时。为确保所有旧 URL 都有效(并且不损害搜索引擎排名),请使用 301 重定向。 301 重定向 can be done in MVC以及 URL 重写。
因此,我的建议是将所有“重写”从 URL Rewrite 切换到路由,因为路由,这不再是真正必要的。您可以设置 configuration with routing这类似于您的重写以达到您的 .ashx页面。
routes.MapPageRoute(
routeName: "RouteASHX",
routeUrl: "{ashxPage}",
physicalFile: "~/{ashxPage}.ashx",
checkPhysicalUrlAccess: false,
defaults: new RouteValueDictionary(),
constraints: new RouteValueDictionary(new { ashxPage = @"customers|orders|products.*" }));
we have only been able to get Web API requests to route if they match a URL Rewrite condition (e.g., the parameterless
api/v2/customersinCustomerControllerworks because it matches<match url="(customers|orders|products.*)"/>, butapi/v2/customers/C123fails because it does not match the rewrite condition).
这是另一个不相关的问题,这是由于使用具有相似路径和未定义顺序的属性路由。属性路由的一个问题是 .NET 属性根据定义具有未定义的顺序。另一方面,路由是设置的,因此 first match always wins .这两个目标是矛盾的,这就是你被咬的地方。参见 Attribute vs Conventional Routing 了解详情。
幸运的是,修复起来并不难。有一个 Order您可以设置该属性以手动覆盖应用 .NET 属性的任意顺序,将更具体的路由放在不太具体的路由之前。
public class CustomersController : ApiController
{
[Route("api/v2/customers", Order = 2)]
[ResponseType(typeof(CustomerCollection))]
public IHttpActionResult Get()
{
...
}
[Route("api/v2/customers/{identifier}", Order = 1)]
[ResponseType(typeof(Customer))]
public IHttpActionResult Get(string identifier)
{
...
}
}
应用此配置将使您的 Web API 无需重写 URL 即可工作。
