[翻译]运用文件解析器在任意文件中使用虚拟应用路径(~)
原文出處:http://www.codeproject.com
?? Using the FileResolver to allow virtual application paths ( ~ ) in any file
引言
??? 如果不是很熟悉“~”在ASP.NET中的使用,你總是完全地忽略它。我們的開發環境經常是和成品的服務器是不同的。我們可以在開發的PC上使用一個虛擬路徑來測試我們的應用程序,而使用一個單獨的頂級網址來發布。
??? 那么,當把image或者link加入到頁面中的時候,你總是必須知道提供的路徑的類型——相對、絕對等等。在ASP.NET中最好的技巧之一就是使用“~”。它實質上是HttpRuntime.AppDomainAppVirtual屬性的一個快捷方式,引用的是虛擬應用程序的根,而不是web服務器的根。
??? “~”是快捷、簡潔的,同時也十分受限制。比如,僅僅在設置那些Render之前知道怎么解析路徑的控件的一些路徑相關屬性時,它是有用的。它只是在ASP.NET的預定義范圍內有用,絕對不能使用在ASP.NET的控管外的文件中。至少目前還是這樣。
??? 我經常碰上的問題之一是:在非ASP.NET文件(如css文件)中設定一個路徑。比如,我想在css文件中設定一個樣式的背景(background-image)屬性時,除非我使用一個相對路徑,否則將是很頭疼的事情。我通常的解決方法是直接增加背景屬性到頁面的標簽里面而不是一個css文件里,我是首先承認這種做法是不令人滿意的人。
??? 兩個解決辦法在我的腦海中浮現。在結合它們之后,我構建了一個可靠的解決方案。我的思路包括使用一個HTTP處理機來截取任何資源請求,諸如css、js文件等等。該處理機負責分析上述的那些資源文件并且將解析后的文件返回給客戶端。
基礎
??? 本文假定你對HTTP處理機有初步的了解。
代碼的使用
??? 針對本文,我打算用css文件來作為例子,當然這個技術也可以很容易的應用到任何其他需要處理的文件類型上。
??? 假設當前有如下一個標簽在a.aspx頁面中來鏈接一個樣式表。
?<link rel="stylesheet" href="resources/stylesheet.css" />
??? 一旦我實現了上面所述的解決方案,上面的標簽將改成如下:
?<link rel="stylesheet" href="~/resources/stylesheet.css.ashx" />
??? 正如你所見到的,在文件路徑上有一個小小的附加后綴“.ashx”,這是為了告訴文件解析器,需要解析css文件中任何的虛擬路徑。
??? 另外一個小小的變化是我們在css文件的路徑上使用了“~”。一會你將看到HTTP處理機會自動地解析這個css文件路徑。然而,它似乎不適用于ASP.NET1.1的場合,因此對于ASP.NET1.1,你可能必須使用一個真實的相對或絕對路徑。
??? 那么,讓我們關注一下文件解析器的代碼。
namespace FileResolverDemoWeb
{
??? public class FileResolver : IHttpHandler
??? {
??????? /// <summary>
??????? /// File cache item used to store file
??????? /// content & date entered into cache
??????? /// </summary>
??????? internal class FileCacheItem
??????? {
??????????? internal string Content;
??????????? internal DateTime DateEntered = DateTime.Now;
??????????? internal FileCacheItem(string content)
??????????? {
??????????????? this.Content = content;
??????????? }
??????? }
??????? private FileCacheItem UpdateFileCache(HttpContext context,
????????????????????????????????????????????????? string filePath)
??????? {
??????????? string content;
??????????? using(FileStream fs = new FileStream(filePath,
???????????????????????????? FileMode.Open, FileAccess.Read))
??????????? {
??????????????? using(StreamReader sr = new StreamReader(fs))
??????????????? {
??????????????????? content = sr.ReadToEnd();
??????????????????? sr.Close();
??????????????? }
??????????????? fs.Close();
??????????? }
??????????? //Get absolute application path
??????????? string relAppPath = HttpRuntime.AppDomainAppVirtualPath;
??????????? if(!relAppPath.EndsWith("/"))
??????????????? relAppPath += "/";
??????????? //Replace virtual paths w/ absolute path
??????????? content = content.Replace("~/", relAppPath);
??????????? FileCacheItem ci = new FileCacheItem(content);
??????????? //Store the FileCacheItem in cache
??????????? //w/ a dependency on the file changing
??????????? CacheDependency cd = new CacheDependency(filePath);
??????????? context.Cache.Insert(filePath, ci, cd);
??????????? return ci;
??????? }
??????? public void ProcessRequest(HttpContext context)
??????? {
??????????? string absFilePath =
??????????????? context.Request.PhysicalPath.Replace(".ashx", "");
???????????
??????????? //If a tilde was used in the page
??????????? //to this file, replace it w/ the app path
??????????? if(absFilePath.IndexOf("~\\") > -1)
??????????????? absFilePath = absFilePath.Replace("~",
????????????????????????????? "").Replace("\\\\", "\\");
??????????? if(!File.Exists(absFilePath))
??????????? {
??????????????? context.Response.StatusCode = 404;
??????????????? return;
??????????? }
??????????? FileCacheItem ci = (FileCacheItem)context.Cache[absFilePath];
??????????? if(ci != null)
??????????? {
??????????????? if(context.Request.Headers["If-Modified-Since"] != null)
??????????????? {
??????????????????? try
??????????????????? {
??????????????????????? DateTime date = DateTime.Parse(
????????????????????????? context.Request.Headers["If-Modified-Since"]);
??????????????????????? if(ci.DateEntered.ToString() == date.ToString())
??????????????????????? {
??????????????????????????? //Don't do anything, nothing
??????????????????????????? //has changed since last request
??????????????????????????? context.Response.StatusCode = 304;
??????????????????????????? context.Response.StatusDescription =
???????????????????????????????????????????????? "Not Modified";
??????????????????????????? context.Response.End();
??????????????????????????? return;
??????????????????????? }
??????????????????? }
??????????????????? catch(Exception){}
??????????????? }
??????????????? else
??????????????? {
??????????????????? //In the event that the browser doesn't
??????????????????? //automatically have this header, add it
??????????????????? context.Response.AddHeader("If-Modified-Since",
??????????????????????????????????????? ci.DateEntered.ToString());
??????????????? }
??????????? }
??????????? else
??????????? {
??????????????? //Cache item not found, update cache
??????????????? ci = UpdateFileCache(context, absFilePath);
??????????? }
??????????? context.Response.Cache.SetLastModified(ci.DateEntered);
??????????? context.Response.ContentType = "text/" +
??????????????????? GetContentType(Path.GetExtension(absFilePath));
??????????? context.Response.Write(ci.Content);
??????????? context.Response.End();
??????? }
??????? /// <summary>
??????? /// Gets the appropriate content type for a specified extension
??????? /// </summary>
??????? private string GetContentType(string ext)
??????? {
??????????? switch(ext.ToLower())
??????????? {
??????????????? case ".css":
??????????????????? return "css";
??????????????????? break;
??????????????? case ".xml":
??????????????????? return "xml";
??????????????????? break;
??????????????? case ".js":
??????????????????? return "javascript";
??????????????????? break;
??????????????? default:
??????????????????? return "plain";
??????????????????? break;
??????????? }
??????? }
??????? #region IHttpHandler Members
??????? public bool IsReusable
??????? {
??????????? get
??????????? {
??????????????? return true;
??????????? }
??????? }
??????? #endregion
??? }
}
??? 我們來分析一下上面的代碼。
??? CacheItem是一個獲取css文件中要解析內容的一個內部類。它有一個DateEntered屬性,用于記錄內容最后被更新的時間。它將決定我們是否需要提供一個新的css文件內容給客戶端。
??? ProcessRequest是一個繼承IHttpHander接口時必須實現的方法,方法里包含了大部分處理機的處理邏輯。在ProcessRequest方法中,我們從HttpContext.Request.PhysicalPath屬性來獲取要處理的文件。我們做個初步檢查來確保文件路徑已經被解析。一旦我們獲得文件的映射實際路徑,我們再檢查一下確保文件在文件系統中是否存在。
string absFilePath = context.Request.PhysicalPath.Replace(".ashx", "");
//If a tilde was used in the page to this file, replace it w/ the app path
if(absFilePath.IndexOf("~\\") > -1)
??? absFilePath = absFilePath.Replace("~", "").Replace("\\\\", "\\");
if(!File.Exists(absFilePath))
{
??? context.Response.StatusCode = 404;
??? return;
}
??? 一旦文件證實存在,我們需要檢查頁面緩存,看看是否有一個相關的CacheItem已經加入,如果這是這個css文件的首次請求,那么我們將創建并儲存一個CacheItem。
??? 如果CacheItem已經存在,我們比較DateEntered和來自請求頭If-Modified-Since的值,如果日期匹配,那么我們知道客戶端有著該文件最新的緩存,如果日期不是匹配的或者沒有找到請求頭,我們就試著加入請求頭并且將新的內容返回給客戶端。
FileCacheItem ci = (FileCacheItem)context.Cache[absFilePath];
if(ci != null)
{
??? if(context.Request.Headers["If-Modified-Since"] != null)
??? {
??????? try
??????? {
??????????? DateTime date = DateTime.Parse(
???????????????? context.Request.Headers["If-Modified-Since"]);
??????????? if(ci.DateEntered.ToString() == date.ToString())
??????????? {
??????????????? //Don't do anything, nothing has
??????????????? //changed since last request
??????????????? context.Response.StatusCode = 304;
??????????????? context.Response.StatusDescription = "Not Modified";
??????????????? context.Response.End();
??????????????? return;
??????????? }
??????? }
??????? catch(Exception){}
??? }
??? else
??? {
??????? //In the event that the browser doesn't
??????? //automatically have this header, add it
??????? context.Response.AddHeader("If-Modified-Since",
??????????????????????????? ci.DateEntered.ToString());
??? }
}
else
{
??? //Cache item not found, update cache
??? ci = UpdateFileCache(context, absFilePath);
}
context.Response.Cache.SetLastModified(ci.DateEntered);
context.Response.ContentType = "text/" +
??????? GetContentType(Path.GetExtension(absFilePath));
context.Response.Write(ci.Content);
context.Response.End();
??? 如果CacheItem沒有找到,我們需要用一個新的CacheItem更新緩存。這包括從css文件中讀取內容和用真實路徑代替所有出現的“~”兩個步驟。之后,我們將新的內容封裝到一個CacheItem中并且儲存它到頁面緩存中。
private FileCacheItem UpdateFileCache(HttpContext context,
????????????????????????????????????????? string filePath)
{
??? string content;
??? using(FileStream fs = new FileStream(filePath,
????????????????????????? FileMode.Open, FileAccess.Read))
??? {
??????? using(StreamReader sr = new StreamReader(fs))
??????? {
??????????? content = sr.ReadToEnd();
??????????? sr.Close();
??????? }
??????? fs.Close();
??? }
??? //Get absolute application path
??? string relAppPath = HttpRuntime.AppDomainAppVirtualPath;
??? if(!relAppPath.EndsWith("/"))
??????? relAppPath += "/";
??? //Replace virtual paths w/ absolute path
??? content = content.Replace("~/", relAppPath);
??? FileCacheItem ci = new FileCacheItem(content);
??? //Store the FileCacheItem in cache
??? //w/ a dependency on the file changing
??? CacheDependency cd = new CacheDependency(filePath);
??? context.Cache.Insert(filePath, ci, cd);
??? return ci;
}
??? 至此,我們就基本完成了文件解析器。和所有處理機一樣,它需要在你的web.config中加入一個附加的條目才能工作。不僅這個是必要的,而且我們還要做一些小小的設定來擴展文件解析器,使之支持任何我們所需的文件類型。
<configuration>
??? <system.web>
??????? <httpHandlers>
??????????? <add verb="GET" path="*.css.ashx"
????????????????? type="FileResolverDemoWeb.FileResolver,FileResolverDemoWeb" />
??????????? <add verb="GET" path="*.js.ashx"
????????????????? type="FileResolverDemoWeb.FileResolver,FileResolverDemoWeb" />
??????? </httpHandlers>
??? </system.web>
</configuration>
??? 在這個場景里面,我解析了css和js文件。
(以下略,翻譯水平有限,歡迎大家指正,如有需要請看原文)
tag: asp.net ~ 文件解析器 路徑
?
轉載于:https://www.cnblogs.com/luyuliang/archive/2006/09/23/512588.html
總結
以上是生活随笔為你收集整理的[翻译]运用文件解析器在任意文件中使用虚拟应用路径(~)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Git Bash推送GitHub不成功-
- 下一篇: 选择日期保存后日期变成前一天(avue