打印本文 打印本文 关闭窗口 关闭窗口
在 WinForm 中完整支持在多级目录中保存的 ASP.NET (转)
作者:武汉SEO闵涛  文章来源:敏韬网  点击数2336  更新时间:2009/4/23 10:45:51  文章录入:mintao  责任编辑:mintao
Request() {
    // wait for at least some input
    if (WaitForRequestBytes() == 0) { // 等待客户端请求数据
      WriteErrorAndClose(400); // 发送 HTTP 400 错误给客户端
      return;
    }

    Request request = new Request(_host, this);
    request.Process();
  }

  private int WaitForRequestBytes() {
    int availBytes = 0;

    try {
      if (_socket.Available == 0) {
        // poll until there is data
        _socket.Poll(100000 /* 100ms */, SelectMode.SelectRead); // 等待客户端数据 100ms 时间
        if (_socket.Available == 0 && _socket.Connected)
          _socket.Poll(10000000 /* 10sec */, SelectMode.SelectRead);
      }

      availBytes = _socket.Available;
    }
    catch {
    }

    return availBytes;
  }


    [6] Request 在接收到 Connection 的请求后,将从客户端读取请求内容,并按照 HTTP 协议进行分析。因为本文不是做 HTTP 协议的分析工作,所以这部分代码就不详细讨论了。
    在 Request.ParseRequestLine 函数分析 HTTP 请求获得请求页面路径后,会调用前面提到过的 Host.IsVirtualPathInApp 函数判断此路径是否在 Web 服务器提供的虚拟路径下级,并且返回此虚拟路径是否指向 ASP.NET 的客户端脚本。如果 Web 请求的虚拟路径以 "/" 结尾,则调用 Request.ProcessDirectoryListingRequest 方法返回列目录的响应;否则调用 HttpRuntime.ProcessRequest 方法完成实际的 ASP.NET 请求处理工作。
    HttpRuntime 通过 Request 的基类 HttpWorkerRequest 提供的统一接口,采用 IoC 的策略获取最终页面的所在。与我前面文章中使用的 SimpleWorkerRequest 实现最大不同在于 Request.MapPath 完成了一个较为完整的虚拟目录到物理目录映射机制。
    SimpleWorkerRequest.MapPath 实现相对简陋:
以下内容为程序代码:

public override string SimpleWorkerRequest.MapPath(string path)
{
  if (!this._hasRuntimeInfo)
  {
    return null;
  }

  string physPath = null;
  string appPhysPath = this._appPhysPath.Substring(0, (this._appPhysPath.Length - 1)); // 去掉末尾斜杠

  if (((path == null) || (path.Length == 0)) || path.Equals("/"))
  {
    physPath = appPhysPath;
  }

  if (path.StartsWith(this._appVirtPath))
  {
    physPath = appPhysPath + path.Substring(this._appVirtPath.Length).Replace(''''/'''', ''''\'''');
  }

  InternalSecurityPermissions.PathDiscovery(physPath).Demand();

  return physPath;
}

    Request.MapPath 的实现则相对要完善许多,考虑了很多 SimpleWorkerRequest 无法处理的情况,使得 Request 的适应性更强。
以下内容为程序代码:

public override String Request.MapPath(String path) {
  String mappedPath = String.Empty;

  if (path == null || path.Length == 0 || path.Equals("/")) {
    // asking for the site root
    if (_host.VirtualPath == "/") {
      // app at the site root
      mappedPath = _host.PhysicalPath;
    }
    else {
      // unknown site root - don''''t point to app root to avoid double config inclusion
      mappedPath = Environment.SystemDirectory;
    }
  }
  else if (_host.IsVirtualPathAppPath(path)) {
    // application path
    mappedPath = _host.PhysicalPath;
  }
  else if (_host.IsVirtualPathInApp(path)) {
    // inside app but not the app path itself
    mappedPath = _host.PhysicalPath + path.Substring(_host.NormalizedVirtualPath.Length);
  }
  else {
    // outside of app -- make relative to app path
    if (path.StartsWith("/"))
      mappedPath = _host.PhysicalPath + path.Substring(1);
    else
      mappedPath = _host.PhysicalPath + path;
  }

  mappedPath = mappedPath.Replace(''''/'''', ''''\'''');

  if (mappedPath.EndsWith("\") && !mappedPath.EndsWith(":\"))
    mappedPath = mappedPath.Substring(0, mappedPath.Length-1);

  return mappedPath;
}


    关于 Cassini 的进一步讨论,可以参考 www.asp.net 上的讨论专版。

    [7] 在 HttRuntime 完成具体的 ASP.NET 页面处理工作后,会通过 Request.SendResponseFromXXX 系列函数,将页面结果返回给客户端。

    虽然 SimpleWorkerRequest.MapPath 方法实现简单,但理论上完全可以处理多级目录的情况。之所以在使用 SimpleWorkerRequest 时,无法处理嵌套目录,是因为 SimpleWorkerRequest 在构造函数中错误地分解了请求的页面所在虚拟目录等信息。
    SimpleWorkerRequest 的两个构造函数,在将请求页面虚拟路径(如"/help/about.aspx")保存后,都调用了 ExtractPagePathInfo 方法对页面路径做进一步的分解工作。
以下内容为程序代码:

private void SimpleWorkerRequest.ExtractPagePathInfo()
{
  int idx = this._page.IndexOf(''''/'''');
  if (idx >= 0)
  {
    this._pathInfo = this._page.Substring(idx);
    this._page = this._page.Substring(0, idx);
  }
}

    this._pathInfo 是为实现 HttpWorkerRequest.GetPathInfo 提供的存储字段。而 GetPathInfo 将返回 URL 中在页面后的路径信息,例如对 "path/virdir/page.html/tail" 将返回 "/tail"。早期的许多 HTTP 客户端程序,如 Delphi 中 WebAction 的分发,都利用了这个路径信息的特性,在 Web 页面或 ISAPI 一级之后,再次进行请求分发。但因为 SimpleWorkerRequest 实现上或者设计上的限制,导致在处理 PathInfo 时会将 "/help/about.aspx" 类似的多级 url 错误切断。最终返回给 HttpRuntime 的 this._path 将变成空字符串,而 this._pathInfo 被设置为 "/help/about.aspx",而单级路径如 "about.aspx" 不受影响。
    知道了这个原理后,就可以对 SimpleWorkerRequest 稍作修改,重载受到 ExtractPagePathInfo 影响的几个方法,即可完成对多级目录结构下页面的支持。如果需要进一步的映射支持,如同时支持多个虚拟子目录,可以参照 Cassini 的 Request 实现 MapPath 等方法。
以下内容为程序代码:

public class Request : SimpleWorkerRequest
{
  private string _appPhysPath;
  private string _appVirtPath;

  private string _page;
  private string _pathInfo;

  public Request(string page, string query, TextWriter output) : base(page, query, output)
  {
    this._appPhysPath = Thread.GetDomain().GetData(".appPath").ToString();
    this._appVirtPath = Thread.GetDomain().GetData(".hostingVirtualPath").ToString();

    this._page = page;

    // TODO: 从 page 中进一步解析 Path Info
  }

  public override string GetPathInfo()
  {
    if (this._pathInfo == null)
    {
      return string.Empty;
    }
    return this._pathInfo;
  }

  private string GetPathInternal(bool includePathInfo)
  {
    string path = (_appVirtPath.Equals("/") ? _page : _appVirtPath + _page);

    if (includePathInfo && (_pathInfo != null))
    {
      return path + this._pathInfo;
    }
    else
    {
      return path;
    }
  }

  public override string GetUriPath()

上一页  [1] [2] [3]  下一页

打印本文 打印本文 关闭窗口 关闭窗口