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

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

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

    空間站

    北極心空

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

    作者:cleverpig


    關(guān)于本指南

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

    Servlet的限制

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

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

    他提出構(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)的用法針對(duì)每個(gè)HTTP請(qǐng)求都創(chuàng)建獨(dú)立的線程進(jìn)行處理。并提出了他對(duì)下一代Servlet技術(shù)的設(shè)想

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

    Restlet簡(jiǎn)介

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

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

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

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

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

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

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

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


    請(qǐng)注意上面的示例使用了最簡(jiǎn)單的方式:通過(guò)通用的客戶端類(generic Client class)調(diào)用。更加靈活的方式是創(chuàng)建一個(gè)新的Request對(duì)象,然后請(qǐng)求客戶端去處理它。下面的示例展示了如何在調(diào)用時(shí)設(shè)置首選項(xiàng)(例如 referrer URI)。當(dāng)然,也可以是接收回應(yīng)時(shí)的首選語(yǔ)言或者媒體類型:
    // 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框架是如何偵聽客戶端請(qǐng)求并作出回應(yīng)的。這里,我們選用了NRE HTTP服務(wù)端connector(例如基于Jetty的HTTP服務(wù)端connector),返回簡(jiǎn)單的字符串表達(dá)式“Hello World!”。請(qǐng)注意在更加實(shí)際的應(yīng)用中,我們可以創(chuàng)建一個(gè)獨(dú)立的類,此類繼承自Restlet類,而不依靠這里的匿名內(nèi)部類。

    Restlet類與Servlet非常相似,并且在RESTful應(yīng)用中處理調(diào)用時(shí)提供了有限的幫助。我們后面將看到一個(gè)提供了一些特定子類的框架,它能夠更抽象、簡(jiǎn)單地進(jìn)行處理。下面讓我們先看一個(gè)簡(jiǎ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)行并啟動(dòng)服務(wù)端,那么你可以打開瀏覽器輸入http://localhost:8182。實(shí)際上,輸入任何的URI都可以工作,你也可以嘗試一下http://localhost:8182/test/tutorial。值得注意的是,如果你從另一臺(tái)服務(wù)器上測(cè)試服務(wù)端,那么就需要將localhost替換為服務(wù)器的IP地址或者它的域名。

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

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

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

    5. Component、virtual hosts和applications

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

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

    讓我們通過(guò)在瀏覽器中輸入http://localhost:8182/trace/abc/def?param=123來(lái)進(jìn)行測(cè)試,得到測(cè)試結(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ù)

    你遇到過(guò)提供靜態(tài)頁(yè)面(類似Javadocs)服務(wù)的web應(yīng)用?如果正在使用,那么可以直接編寫一個(gè)Directory類,而無(wú)需為它建立Apache服務(wù)。請(qǐng)見下面如何使用:
    // 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();

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

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

    7. 訪問(wèn)日志

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

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


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

    8. 顯示錯(cuò)誤頁(yè)

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

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

    9. 對(duì)敏感資源的訪問(wèn)保護(hù)

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

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

    當(dāng)接收到調(diào)用時(shí),開發(fā)者能夠通過(guò)Request.challengeResponse.identifier/secret類中的Guard filter(保護(hù)過(guò)濾器)使用已經(jīng)解析好的證書。過(guò)濾器是一種特殊的Restlet,它能夠在調(diào)用相應(yīng)Restlet之前進(jìn)行預(yù)處理,或者在相應(yīng) Restlet調(diào)用返回后進(jìn)行后期處理。如果你熟知Servlet API,這里的過(guò)濾器概念和Servlet API中的Filter接口非常接近。看一下我們?nèi)绾涡薷膹那暗拇a來(lái)對(duì)目錄訪問(wèn)進(jìn)行訪問(wè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

    請(qǐng)注意:認(rèn)證和授權(quán)的最終結(jié)果是完全可定制的,這只需要通過(guò)authenticate()和authorize()方法便可完成。任何自定義的機(jī)制都能夠被用來(lái)檢查給定的證書是否有效、通過(guò)認(rèn)證的用戶是否被授權(quán)繼續(xù)訪問(wèn)相應(yīng)Restlet。下面是我們簡(jiǎn)單地硬編碼了用戶、密碼對(duì)。為了測(cè)試,我們使用了客戶端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,來(lái)檢查服務(wù)端返回的response。請(qǐng)別忘記了在啟動(dòng)客戶端之前,先執(zhí)行Restlet服務(wù)端程序。請(qǐng)注意,如果你從另一臺(tái)機(jī)器上測(cè)試服務(wù)端,那么在瀏覽器中輸入U(xiǎn)RI時(shí)需要將"localhost"替換為服務(wù)器的IP地址或者域名。由于使用了默認(rèn)接收任何類型URI的 VirtualHost,因此服務(wù)端無(wú)需任何修改。

    10. URI重寫和重定向

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

    首先介紹的工具是Redirector,它能夠?qū)ool URI重寫為另一個(gè)URI,并接著進(jìn)行相應(yīng)的自動(dòng)重定向。這里支持一些重定向類型:通過(guò)客戶端/瀏覽器的外部重定向、類似代理行為的connector重定向。在下面的例子中,我們將基于Google為名為"mysite.org"的站點(diǎn)定義一個(gè)檢索服務(wù)。與URI相關(guān)的"/search"就是檢索服務(wù),它通過(guò)"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);

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

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

    11. 路由器和分層URI

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

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

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

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

    請(qǐng)看下面的具體實(shí)現(xiàn)代碼。在真實(shí)的應(yīng)用中,你可能希望創(chuàng)建單獨(dú)的子類來(lái)代替我們這里使用的匿名類:
    // 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();


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

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

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

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

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

    回到代碼中,我們?cè)谶@里重構(gòu)了Application.createRoot()方法。出于簡(jiǎn)化目的,我們沒(méi)有提供具有靜態(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類。這個(gè)類繼承自org.restlet.resource.Resource類,因此它覆蓋了具有三個(gè)參數(shù)的構(gòu)造方法。此方法初始化了"context"、"request"和"response"屬性。接著,我們使用從"/users/{user} "URI模板中提取出的"user"屬性,將它的值保存在一個(gè)方便使用的成員變量中。然后,我們便可以在整個(gè)application中查找與"user" 相關(guān)的域?qū)ο罅恕W罱K,我們聲明了用于暴露給用戶的表示變量(representation variants),在這個(gè)簡(jiǎn)單的例子中只是文字而已。它將用于在運(yùn)行時(shí)透明地完成一些內(nèi)容導(dǎo)航,以便為每個(gè)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;
        }
    }


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

    結(jié)論

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

    這里是核心表示類:
    image

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

    相關(guān)資源:

    http://www.restlet.org/documentation/1.0/tutorial
    http://www.restlet.org/about/introduction
    posted on 2008-11-28 14:40 蘆葦 閱讀(853) 評(píng)論(0)  編輯  收藏 所屬分類: JAVA其他
    主站蜘蛛池模板: 男女一边桶一边摸一边脱视频免费| 亚洲一区二区影视| 在线观看亚洲成人| 亚洲成a人片在线观看久| 国产做床爱无遮挡免费视频| 嫩草影院在线免费观看| 免费鲁丝片一级观看| 免费被黄网站在观看| 国产成人免费a在线视频色戒| 四虎www免费人成| 日韩免费视频网站| 国产一区二区免费在线| 亚洲av高清在线观看一区二区 | 亚洲日本成本人观看| 亚洲狠狠成人综合网| 亚洲国产精品99久久久久久| 亚洲国产AV一区二区三区四区| 亚洲乱码日产精品一二三| 青青青亚洲精品国产| 农村寡妇一级毛片免费看视频| 一级毛片完整版免费播放一区| 韩国免费a级作爱片无码| 免费无码一区二区三区蜜桃| 亚洲欧洲免费视频| 99re热免费精品视频观看| 女人毛片a级大学毛片免费| 国产区卡一卡二卡三乱码免费| 亚洲精品综合久久| 亚洲AV永久无码区成人网站 | 成人性生活免费视频| 免费A级毛片无码A| 在线观看午夜亚洲一区| 久久水蜜桃亚洲av无码精品麻豆| 精品亚洲成在人线AV无码| 亚洲а∨精品天堂在线| 国产黄色免费观看| 国产国产人免费视频成69堂| 日韩免费一级毛片| 国产亚洲一区二区精品| 亚洲永久在线观看| 一区二区三区在线观看免费|