IIS 与 ASP.NET URL 重写






4.77/5 (13投票s)
一种在虚拟主机和旧版本 IIS 中使用 ASP.NET URL 重写的解决方案。
引言
对于网站来说,拥有用户友好的 URL 非常重要,这有助于搜索引擎优化并改善用户体验。在 ASP.NET 中,可以通过使用 URL 重写来实现漂亮的 URL,但这种应用程序的部署可能会令人烦恼。问题在于,当 IIS 收到类似 mydomain.com/products/3 的请求时,它不会使用 ASP.NET 来处理它。相反,IIS 会尝试在网站文件夹中找到文件 products/3,并向客户端返回错误 404。不幸的是,没有优雅的解决方案,但有一个不太美观的解决方案。
解决方案概念
如果您完全控制托管应用程序的服务器,那么您很幸运 - 只需要让所有请求都由处理 .aspx 文件的同一个 DLL 处理即可。不幸的是,并非总是如此。例如,在使用虚拟主机或 IIS 5 时。所以还有计划 B。我们将网站配置为使用 error404.axd 作为错误 404 页面的 URL。当收到类似 mydomain.com/products/3 的请求时,IIS 将使用 ASP.NET 打开 error404.axd?http://mydomain.com/products/3。这使我们能够使用 URL 重写并修复 URL。
解决方案代码
public class Error404RewritingModule : IHttpModule
{
private static readonly FieldInfo HttpMethodFieldInfo;
private static readonly FieldInfo HttpVerbFieldInfo;
public void Init(HttpApplication context)
{
context.BeginRequest += OnBeginRequest;
}
static void OnBeginRequest(object sender, EventArgs e)
{
HttpContext httpContext = HttpContext.Current;
HttpRequest httpRequest = httpContext.Request;
//
string targetPath = httpRequest.AppRelativeCurrentExecutionFilePath;
if (!targetPath.StartsWith("~/Error404.axd",
StringComparison.InvariantCultureIgnoreCase))
return;
Uri requestedUrl = httpRequest.Url;
var currentApplicationUrl = new Uri(requestedUrl, httpRequest.ApplicationPath);
//
// Extract initially requested url
string initiallyRequestedUrl = ExtractInitiallyRequestedUrl(requestedUrl.Query);
string initiallyRequestedQuery = ExtractInitiallyRequestedQuery(initiallyRequestedUrl);
string initiallyRequestedVirtualPath = GetInitiallyRequestedVirtualPath(
currentApplicationUrl, initiallyRequestedUrl);
if (String.IsNullOrEmpty(initiallyRequestedQuery))
httpContext.RewritePath("~/Hack", "/", "a=b", true);
if (!String.Equals(httpRequest.HttpMethod, "POST",
StringComparison.InvariantCultureIgnoreCase))
if (httpRequest["__VIEWSTATE"] != null ||
httpRequest["__EVENTTARGET"] != null) {
HttpMethodFieldInfo.SetValue(httpRequest, "POST");
HttpVerbFieldInfo.SetValue(httpRequest, 5); // 5 = POST
}
httpContext.RewritePath(initiallyRequestedVirtualPath, null, "", true);
}
...
}
有两个有趣的地方;第一个是以下内容
if (String.IsNullOrEmpty(initiallyRequestedQuery))
httpContext.RewritePath("~/Hack", "/", "a=b", true);
要深入了解 ASP.NET 内部才能解释这一点,但原因如下。如果查询字符串为空,那么 HttpRequest.Current.Url
有时不会被重写所改变。另一个感兴趣的地方是
if (!String.Equals(httpRequest.HttpMethod, "POST",
StringComparison.InvariantCultureIgnoreCase))
if (httpRequest["__VIEWSTATE"] != null || httpRequest["__EVENTTARGET"] != null) {
HttpMethodFieldInfo.SetValue(httpRequest, "POST");
HttpVerbFieldInfo.SetValue(httpRequest, 5); // 5 = POST
}
这是必需的,因为 IIS 6.0 将 GET 请求传递给 Error404.axd,即使客户端发送了 POST。因此,这段代码会修改 HttpRequest
以使其再次成为 POST。
用法
- 将 Xtensive.Web.Deployment.dll 放在应用程序的 bin 文件夹中,并将以下内容添加到
httpModules
配置部分
<httpModules> <add name="LiveUI.Error404-Handler" type="Xtensive.Web.Error404RewritingModule, Xtensive.Web"> </httpModules>
如果您使用虚拟主机,它将如下所示
结论
您可能不喜欢所呈现的解决方案,我也一样,但它总比没有好。最初,我开发这个模块是为了帮助 LiveUI 用户部署他们的应用程序,因此它在 LiveUI 演示中经过测试。但使用其他框架(例如 ASP.NET MVC 或 UrlRewriting.NET)应该不会有问题。