posts - 495,comments - 227,trackbacks - 0
          URI、URL和URN是識別、定位和命名互聯網上的資源的標準途徑。本文分析了URI、URL和URN和Java API的URI和URL類(以及與URL相關的類),并演示了如何在程序中使用這些類。
            1989年Tim Berners-Lee發明了互聯網(World Wide Web)。WWW被認為是全球互連的實際的和抽象的資源的集合--它按需求提供信息實體--通過互聯網訪問。實際的資源的范圍從文件到人,抽象的資源包括數據庫查詢。因為要通過多樣的方式識別資源(人的名字可能相同,然而計算機文件只能通過唯一的路徑名稱組合訪問),所以需要標準的識別WWW資源的途徑。為了滿足這種需要,Tim Berners-Lee引入了標準的識別、定位和命名的途徑:URI、URL和URN。
            URI、URL和URN是什么?
            體系中的URI、URL和URN是彼此關聯的。URI的范疇位于體系的頂層,URL和URN的范疇位于體系的底層。這種排列顯示URL和URN都是URI的子范疇,如圖1所示:

          圖1:
          ?
          URI、URL和URN之間的層次關系。URL和URN是URI的子范疇。
            URI表示的是統一的資源標識,它是以某種統一的(標準化的)方式標識資源的簡單字符串。典型情況下,這種字符串以scheme(命名URI的名字空間的標識符--一組相關的名稱)開頭,語法如下:
            [scheme:] scheme-specific-part
            URI以scheme和冒號開頭。Scheme用大寫/小寫字母開頭,后面為空或者跟著更多的大寫/小寫字母、數字、加號、減號和點號。冒號把scheme與scheme-specific-part分開了,并且scheme-specific-part的語法和語義(意思)由URI的名字空間決定。其中一個例子是http://www.cnn.com,其中http是scheme,//http://www.cnn.com是 scheme-specific-part,并且它的scheme與scheme-specific-part被冒號分開了。
          我們可以把URI按照絕對的或相對的分類。絕對的URI指以scheme(后面跟著冒號)開頭的URI。前面提到的http://www.cnn.com就是絕對的URI的一個例子,其它的例子還有mailto:jeff@javajeff.comnews:comp.lang.java.help和xyz://whatever。你可以把絕對的URI看作是以某種方式引用某種資源,而這種方式對標識符出現的環境沒有依賴。如果使用文件系統作類比,絕對的URI類似于從根目錄開始的某個文件的路徑。與絕對的URI不同的,相對的URI不是以scheme(后面跟著冒號)開始的URI。它的一個例子是articles/articles.html。你可以把相對的URI看作是以某種方式引用某種資源,而這種方式依賴于標識符出現的環境。如果用文件系統作類比,相對的URI類似于從當前目錄開始的文件路徑。
          URI可以進一步分為不透明的和分層的兩類。不透明的URI指scheme-specific-part不是以正斜杠(/)開頭的絕對的URI。其例子有news:comp.lang.java和前面的mailto:jeff@javajeff.com。不透明的URI并不是用于分解的(超出了識別scheme的范疇),因為不需要驗證scheme-specific-part的有效性。與它不同的是,分層的URI可以是以正斜杠開頭的絕對的URI或相對的URL。
            與不透明的URI不同,分層的URI的scheme-specific-part必須被分解為幾個組成部分。這些組成部分是什么?分層的URI標識組件的普通子集的scheme-specific-part符合下面的語法:
            [//authority] [path] [?query] [#fragment]
            可選的authority組件標識了該URI名字空間的命名機構。如果有這一部分,它就是以一對正斜杠開始的,它可以是基于服務器或基于注冊的,并且它以后面的正斜杠、問號或沒有其它符號結束。基于注冊的授權機構組件有特定大綱的語法(本文沒有討論,因為很少使用它),而基于服務器的授權機構組件的語法如下:
            [userinfo@] host [:port]
            按照這種語法,基于服務器的授權機構組件可以隨意的以用戶信息(例如用戶名)開始,后面跟著一個@符號,緊接著是主機的名稱,以及冒號和端口號。例如jeff@x.com:90就是一個基于服務器的授權機構組件,其中jeff包含了用戶信息,x.com包含了主機,90包含了端口。
            可選的path組件根據授權機構組件(如果提供了)或大綱(如果沒有授權機構組件)識別資源的定位(或位置)。路徑(path)可以分成一系列的路徑片斷(path segment),每個路徑片斷使用正斜杠與其它的路徑片斷隔開。如果路徑的第一個路徑片斷以一個正斜杠開始,該路徑就被認為是絕對的。否則路徑就被認為是相對的。例如,/a/b/c由三個路徑片斷--a、b和c組成了一個路徑,此外,這個路徑是絕對的,因為第一個路徑片斷(a)的前綴是正斜杠。
            可選的query組件識別要傳遞給某種資源的數據。這種資源使用該數據獲取或生成其它的傳遞回調用者的數據。例如,http://www.somesite.net/a?x=y, x=y就是一個查詢(query),在這個查詢中,x=y是傳遞給某種資源的數據--x是某種實體的名稱,y是該實體的值。
            最后一個組件是fragment。盡管該組件作為URI的一部分出現,但不是絕對的。當使用URI進行某種檢索操作時,后面執行操作的軟件使用fragment聚焦于軟件感興趣的資源部分(在該軟件成功檢索到資源的數據后)。
            為了實際表現前面提到的組件信息,可以使用下面的URI:
            ftp://george@x.com:90/public/notes?text=shakespeare#hamlet
            上面的URI把ftp識別為大綱,把george@x.com:90識別為基于服務器的授權機構(其中george是用戶信息,x.com是主機,90是端口),把/public/notes識別為路徑,把text=shakespeare識別為查詢,把hamlet識別為片斷。本質上它是一個叫做george的用戶希望通過/public/notes路徑在服務器x.com的90端口上檢索shakespeare文本的hamlet信息。在shakespeare成功的返回到該程序后,程序定位hamlet段并把它呈獻給該用戶。
          標準化可以通過目錄術語來理解。假定目錄x直接位于根目錄之下,x有子目錄a和b,b有文件memo.txt,a是當前目錄。為了顯示memo.txt中的內容(在微軟Windows下),你可能輸入type \x\.\b\memo.txt。你也可能輸入type \x\a\..\b\memo.txt,在這種情況下,a和..的出現是沒有必要的。這兩種形式都不是最簡單的。但是如果輸入\x\b\memo.txt,你就指定了最簡單的路徑了,從根目錄開始訪問memo.txt。最簡單的\x\b\memo.txt路徑就是標準化的路徑。
            通常通過基本的和相對的URI訪問資源。基本的URI是絕對的URI,它唯一地標識了某種資源的名字空間,而相對的URI標識了與基礎的URI相對的資源。(與基本的URI不同,相對的URI在某種資源的生存周期內可以永遠不需要改變)。因為基本的和相對的URI都不能完整的識別某種資源,有必要把兩種URI通過解析過程合并。相反地,通過相對化從合并的URI中提取相對的URI也是可行的。
            注意
            不透明的URI與其它的URI不同,它不服從標準化、分解和相對化。
            假定你把x://a/作為基礎的URI,并把b/c作為相對的URI。根據基礎URI分解這個相對的URI將產生x://a/b/c。根據x://a/相對化x://a/b/c將產生b/c。
            URI不能定位或讀取/寫入資源。這是統一的資源定位器(URL)的任務。URL是一種URI,但是它的大綱組件是已知的網絡協議(簡稱協議),并且它把URI組件與某種協議處理程序(一種資源定位器和根據協議建立的約束規則與資源通訊的讀/寫機制)。
            URI一般不能為資源提供持久不便的名稱。這是統一的資源命名(URN)的任務。URN也是一種URI,但是全球唯一的、持久不便的,即使資源不在存在或不再使用。
          使用URI
            網絡API通過提供了URI類(位于java.net程序包中),使我們在源代碼層使用URI成為可能。URI的構造函數建立了封裝URI的URI對象;URI的方法建立URI對象;如果授權機構組件是基于服務器的就分析它,提取URI組件,決定URI對象的URI是絕對的還是相對的;決定URI對象的URI是不透明的還是分層的;比較兩個URI對象中的URI;標準化(normalize)URI對象的URI;根據URI對象的基礎URI分解某個相對的URI以得到已分解的URI;根據URI對象的基礎URI關聯某個已分解的URI以得到相對的URI,把URI對象轉換為URL對象。
          我們進一步查看URI類,在它里面有五個構造函數。最簡單的是URI(String uri)。這個構造函數把URI作為String類型的參數,把URI分解為組件,并把這些組件存儲在一個新的URI對象中。如果String對象的URI(通過uri引用)違反了RFC 2396的語法規則,其它的四個構造函數URI(String uri)將會產生一個java.net.URISyntaxException對象。
            下面的代碼片斷演示了使用URI(String uri)建立封裝了一個簡單的URI組件的URI對象:
          URI uri = new URI ("
            典型情況下URI構造函數用于建立封裝用戶指定的URI的URI對象。因為用戶可能輸入不正確的URI,所以URI構造函數產生已檢查的URISyntaxException對象。這意味著你的代碼必須明確地嘗試著調用某個URI構造函數并捕捉異常,或者通過在該方法的Throws子句中列舉URISyntaxException以"推卸責任"。
            如果你知道URI是有效的(例如在源代碼中的URI),將不會產生URISyntaxException對象。因為在這種情況下處理某個URI構造函數的異常處理要求可能有困難,所以URI提供了靜態的create(String uri)方法。這個方法分解通過uri引用的String對象中包含URI,如果該URI沒有違反任何語法規則就建立URI對象(并從方法中返回對它的引用),否則將捕捉到一個內部的URISyntaxException對象,把該對象包裝金一個未檢查的IllegalArgumentException對象中,并拋出這個IllegalArgumentException對象。因為IllegalArgumentException是未檢查的,你不需要明確的嘗試代碼并捕捉異常或把它的類名稱列舉在Throws子句中。
            下面的代碼片斷演示了create(String uri):
          URI uri = URI.create ("
            URI構造函數和create(String uri)方法試圖分解出某個URI的授權機構組件的用戶信息、主機和端口部分。對于按正常形式形成的基于服務器的授權機構組件,它們是會成功的。對于按拙劣的形式形成的基于服務器的授權機構組件,他們將會失敗--并且把該授權機構組件當作是基于注冊的。有時你可能知道某個URI的授權機構組件必須是基于服務器的。你可以確保該URI的授權機構組件分解出用戶信息、主機和端口,或者你可以確保將產生一個異常(伴隨著相應的診斷信息)。你可以通過調用URI的parseServerAuthority()方法實現這種操作。如果成功分解出URI,該方法將返回包含提取的用戶信息、主機和端口部分的URI的新URI對象的一個引用(但是如果授權機構組件已經被分解過了,將會返回調用parseServerAuthority()的URI對象的引用。),否則該方法將產生一個URISyntaxException對象。
            下面的代碼片斷演示了parseServerAuthority():
          // 下面的parseServerAuthority()調用出現后會發生什么情況?
          URI uri = new URI ("http://foo:bar").parseServerAuthority();
           一旦擁有了URI對象,你就可以通過調用getAuthority()、getFragment()、getHost()、getPath()、getPort()、getQuery()、getScheme()、getSchemeSpecificPart()和 getUserInfo()方法提取多種組件。你也可以通過調用isAbsolute()確定該URI是絕對的還是相對的,通過調用isOpaque()確定該URI是不透明的還是分層的。如果返回值是true意味著該URI是絕對的或不透明的,如果返回值是false意味著該URI是相對的或分層的。
            列表1中的程序用命令行參數建立了一個URI對象,調用URI組件提取方法來檢索URI的組件,并調用URI的isAbsolute()和isOpaque()方法把該URI分類為絕對的/相對性和不透明的/分層的。
            列表1: URIDemo1.java
          // URIDemo1.java
          import java.net.*;
          class URIDemo1
          {
          public static void main (String [] args) throws Exception
          {
          if (args.length != 1)
          {
          System.err.println ("usage: java URIDemo1 uri");
          return;
          }
          URI uri = new URI (args [0]);
          System.out.println ("Authority = " +uri.getAuthority ());
          System.out.println ("Fragment = " +uri.getFragment ());
          System.out.println ("Host = " +uri.getHost ());
          System.out.println ("Path = " +uri.getPath ());
          System.out.println ("Port = " +uri.getPort ());
          System.out.println ("Query = " +uri.getQuery ());
          System.out.println ("Scheme = " +uri.getScheme ());
          System.out.println ("Scheme-specific part = " +
          uri.getSchemeSpecificPart ());
          System.out.println ("User Info = " +uri.getUserInfo ());
          System.out.println ("URI is absolute: " +uri.isAbsolute ());
          System.out.println ("URI is opaque: " +uri.isOpaque ());
          }
          }
            輸入java URIDemo1命令后,列表1的輸出結果如下:
          query://jeff@books.com:9000/public/manuals/appliances?stove#ge:
          Authority =
          jeff@books.com:9000
          Fragment = ge
          Host = books.com
          Path = /public/manuals/appliances
          Port = 9000
          Query = stove
          Scheme = query
          //jeff@books.com:9000/public/manuals/appliances?stove
          User Info = jeff
          URI is absolute: true
          URI is opaque: false
            上面的輸出顯示該URI是絕對的,因為它指定了一個大綱(query),并且該URI是分層的,因為query后面有/符號。
            技巧
            你應該調用URI的compareTo(Object o)和equals(Object o)來決定URI的次序(為了排序目的)和等同性。你可以參考SDK文檔,查閱這些方法的更多信息。
          URI類支持基本的URI操作,包括標準化(normalization)、分解(resolution)和相對化(relativization)。標準化是通過URI的normalize()方法支持的。調用normalize()時,它返回對新URI對象的引用,該對象包含調用的URI對象的URI的標準的表現。
            列表2演示了normalize()方法。它把URI作為程序的唯一的參數,URIDemo2打印出標準的相等的URI。
            列表2: URIDemo2.java
          // URIDemo2.java
          import java.net.*;
          class URIDemo2
          {
          public static void main (String [] args) throws Exception
          {
          if (args.length != 1)
          {
          System.err.println ("usage: java URIDemo2 uri");
          return;
          }
          URI uri = new URI (args [0]);
          System.out.println ("Normalized URI = " +
          uri.normalize ().toString ());
          }
          }?
            在編譯URIDemo2后,在命令行輸入java URIDemo2 x/y/../z/./q,將看到下面的輸出:
          Normalized URI = x/z/q
            上面的輸出顯示y、..和.消失了。這是因為..意味著你想直接在x下面訪問名字空間的z部分,.意味著你希望訪問與z部分相關的名字空間的q部分。
            URI通過提供resolve(String uri)、resolve(URI uri)和relativize(URI uri)方法支持反向解析和相對化操作。如果uri引用是空的(null)這三個方法都會產生NullPointerException對象。同樣,如果指定的URI違反了RFC 2396語法規則,resolve(String uri)通過的內部的create(String uri)調用間接地產生一個IllegalArgumentException對象。
            列表3的代碼演示了resolve(String uri)和relativize(URI uri)。
            列表3: URIDemo3.java
          // URIDemo3.java
          import java.net.*;
          class URIDemo3
          {
          public static void main (String [] args) throws Exception
          {
          if (args.length != 2)
          {
          System.err.println ("usage: " +
          "java URIDemo3 uriBase uriRelative");
          return;
          }
          URI uriBase = new URI (args [0]);
          System.out.println ("Base URI = " +uriBase.toString ());
          URI uriRelative = new URI (args [1]);
          System.out.println ("Relative URI = " +uriRelative.toString ());
          URI uriResolved = uriBase.resolve (uriRelative);
          System.out.println ("Resolved URI = " +uriResolved.toString ());
          URI uriRelativized = uriBase.relativize (uriResolved);
          System.out.println ("Relativized URI = " +uriRelativized.toString ());
          }
          }
            在編譯URIDemo3后,在命令行輸入java URIDemo3 http://www.somedomain.com/ x/../y. ,輸出如下:
          Base URI = http://www.somedomain.com/
          Relative URI = x/../y
          Resolved URI = http://www.somedomain.com/y
          Relativized URI = y
            上面的輸出顯示相對的URI的x/../y根據基礎URI http://www.somedomain.com/分解并(在內部)標準化,取得了已分解的http://www.somedomain.com/URI。給定該URI和基礎URI,該已分解的URI根據基礎URI相對化獲得了y,它是原始的但是標準的相對的URI。
            技巧
            調用URI的toURL()方法把URI轉換為URL。
            在本周日的專題中我將向讀者介紹如何使用URL以及MIME(多用途的網際郵件擴充協議)的概念以及它如何與URL發生聯系,敬請期待。
          posted on 2006-04-03 15:11 SIMONE 閱讀(577) 評論(0)  編輯  收藏 所屬分類: JAVA
          主站蜘蛛池模板: 望奎县| 曲沃县| 交口县| 全南县| 云和县| 镇安县| 博湖县| 蒙城县| 普洱| 乌拉特中旗| 长兴县| 略阳县| 峨山| 克拉玛依市| 灌云县| 鄂伦春自治旗| 南岸区| 工布江达县| 博湖县| 庆城县| 马山县| 郎溪县| 海安县| 叶城县| 屏山县| 江西省| 兴城市| 那坡县| 呼玛县| 新兴县| 闻喜县| 县级市| 抚松县| 民权县| 东乡族自治县| 曲阜市| 施秉县| 汉阴县| 呼图壁县| 交口县| 连江县|