<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks
    http://www.matrix.org.cn/resource/article/2007-11-30/1312be72-9f14-11dc-bd16-451eadcf4db4.html

    摘要:

    當(dāng)復(fù)雜核心化模式日趨強(qiáng)大之時,面向?qū)ο笤O(shè)計范例已經(jīng)不總是Web開發(fā)中的最佳選擇,Java開發(fā)者需要認(rèn)識到這一點(diǎn),并且在開發(fā)新的Web服務(wù)端或是 AJAX Web客戶端時開始思考更加RESTfully的設(shè)計。Restlet這個開源項(xiàng)目為那些要采用REST結(jié)構(gòu)體系來構(gòu)建應(yīng)用程序的Java開發(fā)者提供了一個具體的解決方案。朋友們,下面就讓我們開始Restlet探索之旅吧!
    Restlet 指南[Matrix社區(qū)試讀版]

    作者:cleverpig


    關(guān)于本指南

    本指南的翻譯工作經(jīng)過了Restlet社區(qū)的官方授權(quán),cleverpig作為貢獻(xiàn)者完成了本文的翻譯和整理工作。在此發(fā)布Matrix社區(qū)試讀版的目的是為了讓更多的技術(shù)愛好者閱讀并提出翻譯中的不足之處,以提高本指南的質(zhì)量,以期修改后正式發(fā)布。

    Servlet的限制

    在2003年末,Jetty Web容器的作者、Servlet規(guī)范的貢獻(xiàn)者:Greg Wilkins在其博客上對Servlet的問題進(jìn)行了如下總計:

        * 沒有對協(xié)議與應(yīng)用之間的關(guān)系進(jìn)行清洗的劃分。
        * 由于在設(shè)計Servlet時存在對阻塞IO的假設(shè),因此不能充分利用非阻塞NIO機(jī)制。
        * 所有的Servlet Web容器對于某些應(yīng)用來講是過度設(shè)計的。

    他提出構(gòu)思新的API規(guī)范,使其能夠真實(shí)地脫離協(xié)議,并定義能夠暴露內(nèi)容和元數(shù)據(jù)的contentlets。這些想法就是Restlet項(xiàng)目創(chuàng)建的靈感源泉。在之后的文章中,Greg Wilkins解釋了為什么當(dāng)前Servlet API限制非阻塞NIO API得到高效使用的詳細(xì)理由:這種傳統(tǒng)的用法針對每個HTTP請求都創(chuàng)建獨(dú)立的線程進(jìn)行處理。并提出了他對下一代Servlet技術(shù)的設(shè)想

    另一個主要問題就是Servlet API鼓勵應(yīng)用開發(fā)者在應(yīng)用或者用戶會話級別直接將session狀態(tài)保存于內(nèi)存中,盡管這看上去不錯,但它造成了Servlet容器擴(kuò)展性和高可用性的主要問題。為了克服這些問題,就必須實(shí)現(xiàn)復(fù)雜的負(fù)載均衡、session復(fù)制、持久化機(jī)制。這導(dǎo)致了可擴(kuò)展性必然成為災(zāi)難。

    Restlet簡介

    當(dāng)復(fù)雜核心化模式日趨強(qiáng)大之時,面向?qū)ο笤O(shè)計范例已經(jīng)不總是Web開發(fā)中的最佳選擇,Java開發(fā)者需要認(rèn)識到這一點(diǎn),并且在開發(fā)新的Web服務(wù)端或是AJAX Web客戶端時開始思考更加RESTfully的設(shè)計。Restlet這個開源項(xiàng)目為那些要采用REST結(jié)構(gòu)體系來構(gòu)建應(yīng)用程序的Java開發(fā)者提供了一個具體的解決方案。它的非常簡單易用的功能和RESTfully的Web框架,這使其成為了Web2.0開發(fā)中的又一利器。好吧,朋友們,下面就讓我們開始Restlet探索之旅吧!

    1. 注冊一個Restlet實(shí)現(xiàn)

    Restlet框架由兩部分構(gòu)成。第一部分是"Restlet API", 這個中立的API完美地實(shí)現(xiàn)了REST概念并簡化了客戶端和服務(wù)端應(yīng)用的調(diào)用處理。在使用它之前,我們還需要一個支持此API的Restlet實(shí)現(xiàn)。Restlet的諸多實(shí)現(xiàn)可以通過開源項(xiàng)目或者商業(yè)產(chǎn)品獲得。
    image

    API與實(shí)現(xiàn)的分離和Servlet API與web容器的分離(就像Jetty或Tomcat)、JDBC API與相應(yīng)JDBC驅(qū)動的分離非常類似。目前,"Noelios Restlet Engine" (縮寫為NRE)是Restle tAPI的參考實(shí)現(xiàn)之一。當(dāng)下載Restlet發(fā)布版本時,API和NRE就綁定在一起,以備隨時使用。如果你需要使用不同的實(shí)現(xiàn),那么只需要添加JAR 文件到classpath,并刪除com.noelios.restlet.jar這個NRE的JAR文件即可。

    API實(shí)現(xiàn)的注冊過程是完全自動的,如果你對此存在疑問,那么請參考JAR規(guī)范。當(dāng)完成實(shí)現(xiàn)裝載工作后,它將自動回調(diào)org.restlet.util.Engine.setInstance()方法,來進(jìn)行自注冊。

    2. 接收Web頁面的內(nèi)容

    正如我們在Restlet介紹中所提到的,Restlet框架即是一個客戶端,又是一個服務(wù)端框架。例如,NRE能夠簡單地通過它的HTTP客戶端 connector(連接器)訪問遠(yuǎn)程資源。在REST中,connector是一種軟件元素,它使兩個component(組件)之間能夠進(jìn)行通訊,其典型的實(shí)現(xiàn)方式是通過某種網(wǎng)絡(luò)協(xié)議完成通訊。NRE提供了多種客戶端connector實(shí)現(xiàn),這些實(shí)現(xiàn)都基于現(xiàn)存的開源項(xiàng)目。在connector一節(jié)中,列舉出了所有可用的客戶端、服務(wù)端connector,并解釋了如何使用和配置它們。

    下面,我們將獲取一個現(xiàn)存資源的表示法(representation )并將其輸出在JVM控制臺:
    // Outputting the content of a Web page
    Client client = new Client(Protocol.HTTP);
    client.get("http://www.restlet.org").getEntity().write(System.out);


    請注意上面的示例使用了最簡單的方式:通過通用的客戶端類(generic Client class)調(diào)用。更加靈活的方式是創(chuàng)建一個新的Request對象,然后請求客戶端去處理它。下面的示例展示了如何在調(diào)用時設(shè)置首選項(xiàng)(例如 referrer URI)。當(dāng)然,也可以是接收回應(yīng)時的首選語言或者媒體類型:
    // Prepare the request
    Request request = new Request(Method.GET, "http://www.restlet.org");
    request.setReferrerRef("http://www.mysite.org");

    // Handle it using an HTTP client connector
    Client client = new Client(Protocol.HTTP);
    Response response = client.handle(request);

    // Write the response entity on the console
    Representation output = response.getEntity();
    output.write(System.out);


    3. 偵聽瀏覽器

    現(xiàn)在,我們將了解一下Restlet框架是如何偵聽客戶端請求并作出回應(yīng)的。這里,我們選用了NRE HTTP服務(wù)端connector(例如基于Jetty的HTTP服務(wù)端connector),返回簡單的字符串表達(dá)式“Hello World!”。請注意在更加實(shí)際的應(yīng)用中,我們可以創(chuàng)建一個獨(dú)立的類,此類繼承自Restlet類,而不依靠這里的匿名內(nèi)部類。

    Restlet類與Servlet非常相似,并且在RESTful應(yīng)用中處理調(diào)用時提供了有限的幫助。我們后面將看到一個提供了一些特定子類的框架,它能夠更抽象、簡單地進(jìn)行處理。下面讓我們先看一個簡單的示例:
    // Creating a minimal Restlet returning "Hello World"
    Restlet restlet = new Restlet() {
        @Override
        public void handle(Request request, Response response) {
            response.setEntity("Hello World!", MediaType.TEXT_PLAIN);
        }
    };

    // Create the HTTP server and listen on port 8182
    new Server(Protocol.HTTP, 8182, restlet).start();


    如果你運(yùn)行并啟動服務(wù)端,那么你可以打開瀏覽器輸入http://localhost:8182。實(shí)際上,輸入任何的URI都可以工作,你也可以嘗試一下http://localhost:8182/test/tutorial。值得注意的是,如果你從另一臺服務(wù)器上測試服務(wù)端,那么就需要將localhost替換為服務(wù)器的IP地址或者它的域名。

    4. REST架構(gòu)概述

    讓我們先從REST的視角審視一下典型的web架構(gòu)。在下面的圖表中,端口代表了connector,而后者負(fù)責(zé)component之間的通訊(組件在圖中被表示為大盒子)。鏈接代表了用于實(shí)際通訊的特定協(xié)議(HTTP,SMTP等)。
    image

    請注意,同一個component能夠具有任何數(shù)量的客戶端/服務(wù)端connector。例如,Web服務(wù)器B就具有一個用于回應(yīng)用戶代理組件(User Agent component)的服務(wù)端connector,和多個發(fā)送請求到其它服務(wù)端的客戶端connector。

    5. Component、virtual hosts和applications

    另外,為了支持前面所表述的標(biāo)準(zhǔn)REST軟件架構(gòu)元素,Restlet框架也提供了一套類:它們極大地簡化了在單一JVM中部署多個應(yīng)用的工作。其目的在于提供一種RESTful、可移植的、比現(xiàn)存的Servlet API更加靈活的框架。在下面的圖表中,我們將看到三種Restlet,它們用于管理上述復(fù)雜情況:Components能夠管理多個Virtual Hosts和Applications。Virtual Hosts支持靈活的配置,例如同一個IP地址能夠分享多個域名、使用同一個域名實(shí)現(xiàn)跨越多個IP地址的負(fù)載均衡。最后,我們使用應(yīng)用去管理一套相關(guān)的 Restlet、Resource、Representations。另外,應(yīng)用確保了在不同Restlet實(shí)現(xiàn)、不同Virtual Hosts之上的可移植性和可配置性。這三種Restlet的協(xié)助為我們提供了眾多的功能:譬如訪問日志、請求自動解碼、配置狀態(tài)頁設(shè)置等。image

    為了展示這些類,讓我們嘗試一個簡單的示例。首先,我們創(chuàng)建一個component,然后在其上添加一個HTTP服務(wù)端connector,并偵聽 8182端口。接著創(chuàng)建一個簡單的、具有追蹤功能的Restlet,將它放置到組件默認(rèn)的Virtual Hosts上。這個默認(rèn)的主機(jī)將捕捉那些沒有路由到指定Virtual Hosts的請求(詳見Component.hosts屬性)。在后面的一個示例中,我們還將介紹應(yīng)用類的使用方法。請注意,目前你并不能在控制臺輸出中看到任何的訪問日志。
    // Create a new Restlet component and add a HTTP server connector to it
    Component component = new Component();
    component.getServers().add(Protocol.HTTP, 8182);

    // Create a new tracing Restlet
    Restlet restlet = new Restlet() {
        @Override
        public void handle(Request request, Response response) {
            // Print the requested URI path
            String message = "Resource URI  : " + request.getResourceRef()
                    + '\n' + "Root URI      : " + request.getRootRef()
                    + '\n' + "Routed part   : "
                    + request.getResourceRef().getBaseRef() + '\n'
                    + "Remaining part: "
                    + request.getResourceRef().getRemainingPart();
            response.setEntity(message, MediaType.TEXT_PLAIN);
        }
    };

    // Then attach it to the local host
    component.getDefaultHost().attach("/trace", restlet);

    // Now, let's start the component!
    // Note that the HTTP server connector is also automatically started.
    component.start();

    讓我們通過在瀏覽器中輸入http://localhost:8182/trace/abc/def?param=123來進(jìn)行測試,得到測試結(jié)果如下:
    Resource URI  : http://localhost:8182/trace/abc/def?param=123
    Root URI      : http://localhost:8182/trace
    Routed part   : http://localhost:8182/trace
    Remaining part: /abc/def?param=123


    6. 為靜態(tài)文件提供服務(wù)

    你遇到過提供靜態(tài)頁面(類似Javadocs)服務(wù)的web應(yīng)用?如果正在使用,那么可以直接編寫一個Directory類,而無需為它建立Apache服務(wù)。請見下面如何使用:
    // Create a component
    Component component = new Component();
    component.getServers().add(Protocol.HTTP, 8182);
    component.getClients().add(Protocol.FILE);

    // Create an application
    Application application = new Application(component.getContext()) {
        @Override
        public Restlet createRoot() {
            return new Directory(getContext(), ROOT_URI);
        }
    };

    // Attach the application to the component and start it
    component.getDefaultHost().attach("", application);
    component.start();

    正如你所注意到的,我們通過傳遞應(yīng)用的父組件上下文(context)的方式來實(shí)例化應(yīng)用,而不是在第5章中提到的代碼那樣簡單。而其主要原因是應(yīng)用在分配客戶端請求時,請求的分配工作需要客戶端connector來完成,而后者被component所控制,并在所有被包含其中的應(yīng)用之間貢獻(xiàn)。

    為了運(yùn)行此示例,你需要為ROOT_URI提供一個有效值,該值依賴于你的Restlet安裝路徑。默認(rèn)情況下,它被設(shè)置為"file:///D: /Restlet/www/docs/api/"。請注意,這里不需要任何附加的配置。如果你希望自定義在文件擴(kuò)展名和元數(shù)據(jù)(metadata,包括媒體類型、語言、編碼等)之間的映射,或是提供一個與眾不同的索引名,你可以使用應(yīng)用的“metadataService”屬性。

    7. 訪問日志

    有目的地記錄web應(yīng)用的活動是一種常見的需求。Restlet組件能夠在默認(rèn)的情況下生成類似Apache風(fēng)格的日志、甚至自定義日志。通過使用 JDK內(nèi)置的日志功能,logger能夠配置為像任何標(biāo)準(zhǔn)JDK日志那樣過濾信息、對它們進(jìn)行重新格式化或者發(fā)送它們到指定位置。并且支持日志的循環(huán)(rotation);細(xì)節(jié)請查看java.util.logging包。

    值得注意的是,你能夠通過修改component的"logService"屬性來為java.util.logging框架自定義logger名。如果希望完全掌控日志的配置,你需要通過設(shè)置系統(tǒng)屬性來聲明一個配置文件:
    System.setProperty("java.util.logging.config.file", "/your/path/logging.config");


    關(guān)于配置文件格式的細(xì)節(jié),請查看JDK的LogManager類。

    8. 顯示錯誤頁

    另外一個常見的需求是:在調(diào)用處理過程中某些期望結(jié)果沒有出現(xiàn)時,能夠自定義返回的狀態(tài)頁面。也許它是某個資源沒有找到或者一個可接受的表示是無效的。在這種情況下,或者遇到任何無法處理的異常時,Application或者Component將自動提供一個默認(rèn)的狀態(tài)頁面。此服務(wù)與 org.restlet.util.StatusService類相關(guān)聯(lián),并可以作為被稱為“statusService”的Application或者 Component的屬性而被訪問。

    為了自定義默認(rèn)的信息,你只需要簡單地創(chuàng)建StatusService類的子類,并覆蓋其getRepresentation(Status, Request, Response)方法。然后設(shè)置這個類的實(shí)例為指定的“statusService”屬性即可。

    9. 對敏感資源的訪問保護(hù)

    當(dāng)你需要保護(hù)對某些Restlet的訪問時,可以使用下面的方法:一種通用的方法是依靠cookie來識別客戶端(或者客戶端session),并根據(jù)你的應(yīng)用狀態(tài)檢查給定的用戶ID或者session ID,從而判斷次訪問是否被允許。Restlet通過訪問Request或者Response中的CookieCookieSetting對象支持cookie。

    另一種方法是基于標(biāo)準(zhǔn)HTTP認(rèn)證機(jī)制。Neolios Restlet引擎目前允許基于簡單HTTP方案的證書發(fā)送、接收和基于Amazon Web服務(wù)方案的證書發(fā)送。

    當(dāng)接收到調(diào)用時,開發(fā)者能夠通過Request.challengeResponse.identifier/secret類中的Guard filter(保護(hù)過濾器)使用已經(jīng)解析好的證書。過濾器是一種特殊的Restlet,它能夠在調(diào)用相應(yīng)Restlet之前進(jìn)行預(yù)處理,或者在相應(yīng) Restlet調(diào)用返回后進(jìn)行后期處理。如果你熟知Servlet API,這里的過濾器概念和Servlet API中的Filter接口非常接近。看一下我們?nèi)绾涡薷膹那暗拇a來對目錄訪問進(jìn)行訪問保護(hù):
    // Create a Guard
    Guard guard = new Guard(getContext(),
            ChallengeScheme.HTTP_BASIC, "Tutorial");
    guard.getSecrets().put("scott", "tiger".toCharArray());

    // Create a Directory able to return a deep hierarchy of files
    Directory directory = new Directory(getContext(), ROOT_URI);
    guard.setNext(directory);
    return guard;

    image

    請注意:認(rèn)證和授權(quán)的最終結(jié)果是完全可定制的,這只需要通過authenticate()和authorize()方法便可完成。任何自定義的機(jī)制都能夠被用來檢查給定的證書是否有效、通過認(rèn)證的用戶是否被授權(quán)繼續(xù)訪問相應(yīng)Restlet。下面是我們簡單地硬編碼了用戶、密碼對。為了測試,我們使用了客戶端Restlet API:
    // Prepare the request
    Request request = new Request(Method.GET, "http://localhost:8182/");

    // Add the client authentication to the call
    ChallengeScheme scheme = ChallengeScheme.HTTP_BASIC;
    ChallengeResponse authentication = new ChallengeResponse(scheme,
            "scott", "tiger");
    request.setChallengeResponse(authentication);

    // Ask to the HTTP client connector to handle the call
    Client client = new Client(Protocol.HTTP);
    Response response = client.handle(request);

    if (response.getStatus().isSuccess()) {
        // Output the response entity on the JVM console
        response.getEntity().write(System.out);
    } else if (response.getStatus()
            .equals(Status.CLIENT_ERROR_UNAUTHORIZED)) {
        // Unauthorized access
        System.out
                .println("Access authorized by the server, " +
                        "check your credentials");
    } else {
        // Unexpected status
        System.out.println("An unexpected status was returned: "
                + response.getStatus());
    }

    你可以修改這里的user ID或者password,來檢查服務(wù)端返回的response。請別忘記了在啟動客戶端之前,先執(zhí)行Restlet服務(wù)端程序。請注意,如果你從另一臺機(jī)器上測試服務(wù)端,那么在瀏覽器中輸入URI時需要將"localhost"替換為服務(wù)器的IP地址或者域名。由于使用了默認(rèn)接收任何類型URI的 VirtualHost,因此服務(wù)端無需任何修改。

    10. URI重寫和重定向

    Restlet框架的另一個優(yōu)點(diǎn)是對cool URI的內(nèi)建支持。Jacob Nielsen在他的AlertBox中給出了對URI設(shè)計的重要性的絕佳描述

    首先介紹的工具是Redirector,它能夠?qū)ool URI重寫為另一個URI,并接著進(jìn)行相應(yīng)的自動重定向。這里支持一些重定向類型:通過客戶端/瀏覽器的外部重定向、類似代理行為的connector重定向。在下面的例子中,我們將基于Google為名為"mysite.org"的站點(diǎn)定義一個檢索服務(wù)。與URI相關(guān)的"/search"就是檢索服務(wù),它通過"kwd"參數(shù)接收一些檢索關(guān)鍵字:
    // Create an application
    Application application = new Application(component.getContext()) {
        @Override
        public Restlet createRoot() {
            // Create a Redirector to Google search service
            String target =
               "http://www.google.com/search?q=site:mysite.org+{keywords}";
            return new Redirector(getContext(), target,
                    Redirector.MODE_CLIENT_TEMPORARY);
        }
    };

    // Attach the application to the component's default host
    Route route = component.getDefaultHost().attach("/search", application);

    // While routing requests to the application, extract a query parameter
    // For instance :
    // http://localhost:8182/search?kwd=myKeyword1+myKeyword2
    // will be routed to
    // http://www.google.com/search?q=site:mysite.org+myKeyword1%20myKeyword2
    route.extractQuery("keywords", "kwd", true);

    請注意,Redirector只需要三個參數(shù)。第一個參數(shù)是父級上下文,第二個參數(shù)定義了如何基于URI模板重寫URI。這里的URI模板將被Template類處理。第三個參數(shù)定義了重定向類型:出于簡化的目的,我們選擇了客戶端重定向。

    同時,當(dāng)調(diào)用被傳遞給application時,我們使用了Route類從request中提取查詢參數(shù)“kwd”。如果發(fā)現(xiàn)參數(shù),參數(shù)將被復(fù)制到request的“keywords”屬性中,以便Redirector在格式化目標(biāo)URI時使用。

    11. 路由器和分層URI

    作為Redirector的補(bǔ)充,我們還具有另一個管理cool URI的工具:Router(路由器)。它們是一種特殊的Restlet,能夠使其它Restlet(例如Finder和Filter)依附于它們,并基于URI模板進(jìn)行自動委派調(diào)用(delegate call)。通常,你可以將Router設(shè)置為Application的根。

    這里,我們將解釋一下如何處理下面的URI模板:

       1. /docs/ 用于顯示靜態(tài)文件
       2. /users/{user} 用于顯示用戶帳號
       3. /users/{user}/orders 用于顯示特定用戶的所有訂單
       4. /users/{user}/orders/{order} 用于顯示特定的訂單

    實(shí)際上,這些URI包含了可變的部分(在大括號中)并且沒有文件擴(kuò)展名,這在傳統(tǒng)的web容器中很難處理。而現(xiàn)在,你只需要做的只是使用URI模板將目標(biāo)Restlet附著到Router上。在Restlet框架運(yùn)行時,與request的URI最為匹配的Route將接收調(diào)用,并調(diào)用它所附著的 Restlet。同時,request的屬性表也將自動更新為URI模板變量。
    image

    請看下面的具體實(shí)現(xiàn)代碼。在真實(shí)的應(yīng)用中,你可能希望創(chuàng)建單獨(dú)的子類來代替我們這里使用的匿名類:
    // Create a component
    Component component = new Component();
    component.getServers().add(Protocol.HTTP, 8182);
    component.getClients().add(Protocol.FILE);

    // Create an application
    Application application = new Application(component.getContext()) {
        @Override
        public Restlet createRoot() {
            // Create a root router
            Router router = new Router(getContext());

            // Attach a guard to secure access to the directory
            Guard guard = new Guard(getContext(),
                    ChallengeScheme.HTTP_BASIC, "Restlet tutorial");
            guard.getSecrets().put("scott", "tiger".toCharArray());
            router.attach("/docs/", guard);

            // Create a directory able to expose a hierarchy of files
            Directory directory = new Directory(getContext(), ROOT_URI);
            guard.setNext(directory);

            // Create the account handler
            Restlet account = new Restlet() {
                @Override
                public void handle(Request request, Response response) {
                    // Print the requested URI path
                    String message = "Account of user \""
                            + request.getAttributes().get("user") + "\"";
                    response.setEntity(message, MediaType.TEXT_PLAIN);
                }
            };

            // Create the orders handler
            Restlet orders = new Restlet(getContext()) {
                @Override
                public void handle(Request request, Response response) {
                    // Print the user name of the requested orders
                    String message = "Orders of user \""
                            + request.getAttributes().get("user") + "\"";
                    response.setEntity(message, MediaType.TEXT_PLAIN);
                }
            };

            // Create the order handler
            Restlet order = new Restlet(getContext()) {
                @Override
                public void handle(Request request, Response response) {
                    // Print the user name of the requested orders
                    String message = "Order \""
                            + request.getAttributes().get("order")
                            + "\" for user \""
                            + request.getAttributes().get("user") + "\"";
                    response.setEntity(message, MediaType.TEXT_PLAIN);
                }
            };

            // Attach the handlers to the root router
            router.attach("/users/{user}", account);
            router.attach("/users/{user}/orders", orders);
            router.attach("/users/{user}/orders/{order}", order);

            // Return the root router
            return router;
        }
    };

    // Attach the application to the component and start it
    component.getDefaultHost().attach(application);
    component.start();


    請注意,變量的值是直接從URI中提取的,因此這是沒有精確解碼的。為了實(shí)現(xiàn)這樣的工作,請查看手冊中的decode(String)方法

    12. 抵達(dá)目標(biāo)資源

    在前面的示例中,在從目標(biāo)URI中提取那些有趣部分時,我們利用了Restlet框架非常靈活的路由特性對request進(jìn)行路由。但是,我們沒有注意request方法和客戶端對于它所期望的response的偏好。于是,我們?nèi)绾尾拍軐estlet處理器和后臺系統(tǒng)、域?qū)ο舐?lián)系在一起呢?

    到目前為止,我們已經(jīng)介紹了一些在Restlet中超越傳統(tǒng)Servlet API的特性。但我們并沒有在"Restlet"這個框架名稱中使用"REST"。如果你還沒有做的話,我推薦你學(xué)習(xí)一些關(guān)于REST架構(gòu)風(fēng)格和將其應(yīng)用于Web應(yīng)用的最佳實(shí)踐。這里提供了相關(guān)的FAQ記錄,希望能給你一些啟示,同時我們也運(yùn)營著很有用的REST搜索引擎(基于Google)。如果你對傳統(tǒng)MVC框架有一定了解,那么你可以閱讀一下另一個FAQ記錄,它提供了對MVC與Restlet關(guān)系的詳細(xì)說明。
    image

    總結(jié)一下,request中含有標(biāo)識目標(biāo)資源的URI,而目標(biāo)資源就是調(diào)用的主旨。這種資源信息被保存在Request.resourceRef屬性中,并能夠像我們之前所見那樣服務(wù)于路由機(jī)制。因此在處理request時的首要目標(biāo)就是發(fā)現(xiàn)目標(biāo)資源。。。Resource類的實(shí)例或者其子類中的某個。為了幫助我們完成此項(xiàng)任務(wù),我們可以使用專用的Finder,一個Restlet子類,它將Resource類引用作為參數(shù)并在request到來時自動實(shí)例化它。然后Finder將動態(tài)將調(diào)用分配給最新創(chuàng)建的實(shí)例,實(shí)際上就是根據(jù)request方法調(diào)用它的handle*()方法中的某一個(handleGet,handleDelete等)。當(dāng)然,我們可以自定義這種行為,甚至使用Router的attach()方法,將URI模板和 Resource類作為其參數(shù)透明地創(chuàng)建Finder!現(xiàn)在,讓我們看一下展示了示例中主框架類之間關(guān)系的全景圖表:

    回到代碼中,我們在這里重構(gòu)了Application.createRoot()方法。出于簡化目的,我們沒有提供具有靜態(tài)文件的目錄。你可以發(fā)現(xiàn)將Resource類直接指派給Router的方法。
    // Create a router
    Router router = new Router(getContext());

    // Attach the resources to the router
    router.attach("/users/{user}", UserResource.class);
    router.attach("/users/{user}/orders", OrdersResource.class);
    router.attach("/users/{user}/orders/{order}",
            OrderResource.class);

    // Return the root router
    return router;


    我們最后將重審一下UserResource類。這個類繼承自org.restlet.resource.Resource類,因此它覆蓋了具有三個參數(shù)的構(gòu)造方法。此方法初始化了"context"、"request"和"response"屬性。接著,我們使用從"/users/{user} "URI模板中提取出的"user"屬性,將它的值保存在一個方便使用的成員變量中。然后,我們便可以在整個application中查找與"user" 相關(guān)的域?qū)ο罅恕W罱K,我們聲明了用于暴露給用戶的表示變量(representation variants),在這個簡單的例子中只是文字而已。它將用于在運(yùn)行時透明地完成一些內(nèi)容導(dǎo)航,以便為每個request選擇適合的變量,所有這些工作都是透明的。
    public class UserResource extends Resource {
        String userName;

        Object user;

        public UserResource(Context context, Request request,
                Response response) {
            super(context, request, response);
            this.userName = (String) request.getAttributes().get("user");
            this.user = null; // Could be a lookup to a domain object.

            // Here we add the representation variants exposed
            getVariants().add(new Variant(MediaType.TEXT_PLAIN));
        }

        @Override
        public Representation getRepresentation(Variant variant) {
            Representation result = null;
            if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
                result = new StringRepresentation("Account of user \""
                        + this.userName + "\"");
            }
            return result;
        }
    }


    你可以查看本指南中提供的代碼包并對應(yīng)用進(jìn)行測試,并能夠以僅接受Get請求的方式獲得在第十一章中的相同行為。如果你希望使用PUT方法,那么就需要在UserResource中創(chuàng)建一個"allowPut()"方法并簡單地返回"true",并且添加一個"put (Representation)"方法來處理調(diào)用。關(guān)于詳細(xì)內(nèi)容請查閱Restlet的Javadocs。

    結(jié)論

    我們已經(jīng)涵蓋了Restlet框架的許多方面。在你打算行動之前,讓我們先回顧一下展示了本指南的主要概念和它們之間關(guān)系的兩個層次圖表:
    image

    這里是核心表示類:
    image

    除了本指南,你最好的信息來源就是Restlet API的Javadocs、Restlet擴(kuò)展和NRE。還可以閱讀一下connector一節(jié),它列舉出了客戶端和服務(wù)端connector,并解釋了如何使用、配置它們。集成一節(jié)列出了提供可插入特性的所有可用擴(kuò)展:例如與servlet容器的集成、動態(tài)表示的生成等。你還可以在我們的討論組中提出問題并幫助別人。

    相關(guān)資源:

    http://www.restlet.org/documentation/1.0/tutorial
    http://www.restlet.org/about/introduction
    posted on 2008-11-28 14:40 蘆葦 閱讀(853) 評論(0)  編輯  收藏 所屬分類: JAVA其他
    主站蜘蛛池模板: 亚洲国产高清美女在线观看| 久久久久久精品免费免费自慰| 亚洲一区电影在线观看| 亚洲日韩VA无码中文字幕 | 免费在线观看黄网| 免费人成在线观看69式小视频| 国产精品内射视频免费| 亚洲日韩国产二区无码| 亚洲视频在线观看免费视频| 亚洲热妇无码AV在线播放| 国产一区视频在线免费观看| 一个人看www在线高清免费看| 久久成人无码国产免费播放| xvideos永久免费入口| 激情小说亚洲图片| 亚洲欧洲无码AV不卡在线| 亚洲国产成人久久99精品| 亚洲人成在线电影| 亚洲AV永久青草无码精品| 亚洲精品国产自在久久| 四虎影视精品永久免费网站| 大学生a级毛片免费观看| 精品免费久久久久久久| 最近中文字幕免费2019| 91在线手机精品免费观看| 日韩免费人妻AV无码专区蜜桃 | 国产一级在线免费观看| 亚洲高清免费视频| 九九免费久久这里有精品23| 香蕉视频在线观看免费| 在线观看亚洲精品专区| 福利片免费一区二区三区| 久久久久久久久无码精品亚洲日韩 | 亚洲黄色三级视频| 亚洲色图.com| 亚洲综合一区二区精品久久| 亚洲网站在线免费观看| 亚洲制服丝袜在线播放| 丁香婷婷亚洲六月综合色| 一本色道久久88亚洲精品综合 | 久久久久免费看黄A片APP|