程式碼高𠅙

2009/02/24

如何取得 Web 應用程式的根目錄 Context URL

我們常常在開發 Web 應用程式時,會有需要取得 Context URL。例如,為了透過這個 URL 取得某些資源。Context URL,也就是 Web 應用程式根目錄的 URL。例如,給定一個 URL 如下:

    http://test.com:8080/AppContext/path/to/your/page

則 Context URL 指的是:

    http://test.com:8080/AppContext   

如果我們都是以相對路徑 (相對於根目錄或或目前路徑) 的方式來存取資源,那直接使用 Context URL 的必要性就比較低了。不過有時候並無法使用相對路徑,例如:

  • 因為配合 API 的呼叫,你不曉得這個 API 會在虛擬路徑的第幾層呼叫,因此就無法提供相對路徑。
  • 因為配合某種 code gen 機制,你要傳入一個 URL。
  • 因為採用了某種 URL dispatch 的機制,導致你網頁相對路徑發生偏差。

當然還有其他原因。而面對這個問題的解法,傳統上是在 web.config 或 web.xml 裡面去定義一個類似 HOST_URL 的參數來解決。但缺點是,每次 deploy 時,就得變更 HOST_URL 的設定,除了顯得不便之外,因會增加 API 與環境變數的相依性,實在不能算是很好的做法。因此透過程式自動抓取 ContextURL 的作法,便有其必要。

現在,我們假設我們的 ContextURL 就是以上的形式,而不是 WebServer 的根目錄 (例如 http://test.com:8080/),那麼取得 Context URL 的作法,在一般情況下並不難。首先看看 ASP.NET/C# 版本:

   int idx = Request.Url.AbsoluteUri.IndexOf(Request.ApplicationPath);
   string serviceUri = Request.Url.AbsoluteUri.Substring(0, idx); // 伺服器 URL
   string applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL

JSP/Java 版本如下:

   int idx = request.getRequestURL().toString().indexOf(request.getContextPath());
   String serviceUri = request.getRequestURL().substring(0, idx); // 伺服器 URL
   String applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL

不過有些情況比較特殊,例果透過某種 URL dispatch 的機制來存取網頁,你會發現用以上方式取得的 URL,與 Client 端發送的 URL 並不相同。我們以 IBM Tivoli Access Manager 為例,它的 URL 組成格式如下:

      http://sso.com/JUNCTION/AppContext/path/to/your/page
  • sso.com: 是一個用來作 singal sign-on 的網站
  • JUNCTION: 用來試別要將 client 端 request dispatch 到後端哪個實發處理的網站
  • /AppContext/path/to/your/page: 後端處理要求的網頁或程式,實際位址可能是 http://192.168.1.111:8080 /AppContext/path/to/your/page 之類的。

也就是說,當 Client 端發送 http://sso.com/JUNCTION/AppContext/path/to/your/page 這個請求時,經過 Tivoli Access Manager 轉送後,Server 端實際取得的服務位址是 http://192.168.1.111:8080/AppContext/path/to/your/page。

為了解決這個問題,我們需要動用到 HTTP Header 裡面的一個參數 -- referer。referer 是指目前存取這個網頁,是透過哪個網頁連結過來的。有了這層認識,我們可以將上面的程式碼改成如下。 ASP.NET/C# 版本:

   if(IsPostBack) {
      idx = Request.UrlReferrer.AbsoluteUri.IndexOf(Request.ApplicationPath);
      serviceUri = Request.UrlReferrer.AbsoluteUri.Substring(0, idx); // 伺服器 URL
      applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL
   }

JSP/Java 版本如下:

   if(request.getParameter("IsPostBack").equals("true")){
      idx = request.getHeader("referer").indexOf(request.getContextPath());
      serviceUri = request.getHeader("referer").substring(0, idx); // 伺服器 URL
      applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL
   }

如果你是直接在瀏覽器的網址列輸入 URL,或是點選書籤/我的最愛直接連結,那 referer 的值會是 null。因此,我們可以透過檢查這個網頁是否是 post back 網頁,來確保 referer 有值。注意一下,JSP 裡面並沒有預設的 IsPostBack 檢查機制,你要自己做。

另外,有些防火牆或防毒軟體也可能會改寫 Client 端發出的 referer,使得 Server 上取得的值為 null。若是這種情況,可以試著透過 client 端的 Javascript,取得 document.location.href 的值,將它指定給某個隱藏表單欄位,在 post 時傳給 Server 端來加以解決。