65.9K
CodeProject 正在变化。 阅读更多。
Home

IIS 与 ASP.NET URL 重写

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (13投票s)

2009年11月23日

CPOL

2分钟阅读

viewsIcon

69721

downloadIcon

597

一种在虚拟主机和旧版本 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>
  • 配置网站以使用 error404.axd 作为错误 404(错误 405)页面 URL。如果您使用 IIS,它将如下所示
  • 如果您使用虚拟主机,它将如下所示

结论

您可能不喜欢所呈现的解决方案,我也一样,但它总比没有好。最初,我开发这个模块是为了帮助 LiveUI 用户部署他们的应用程序,因此它在 LiveUI 演示中经过测试。但使用其他框架(例如 ASP.NET MVCUrlRewriting.NET)应该不会有问题。

© . All rights reserved.