Restlet第一個Resource(翻譯)

貓咪繼續翻譯Restlet教程。看了這篇文章,應該對Restlet框架有所瞭解。

Restlet第一個Resource
目錄
這篇文章說明Resource如何處理GET,POST,PUT和DELETE方法。
1.引言
2.例子應用
3.實現Items Resource
4.實現Item Resource
5.實現Base Resource
6.運行應用
7.客戶端應用
8.結尾

引言
在開始開發前,我們需要簡單介紹一下Restlet框架的Resource概念。REST告訴我們,Resource根據URL進行辨認,同時能夠有一種或多種表現(也被稱作變異體)呈現(Oaxaca的天氣),能夠響應處理方法。
在Restlet框架中,服務器連接的標準響應由Resource的實例對象最終處理。一個Resource負責聲明支持的表現方式列表(Variant對象的實例)和實現你想要支持的REST方法。
 GET依賴可更改的“variants”列表和“getRepresentation(Variant)”方法。
 POST依賴“allowPost”方法和“post(Representation)”方法。
 DELETE依賴“allowPut”方法和“put(Representation)”方法。
 DELETE依賴“allowDelete”方法和“delete()”方法。
還有,每一個到達的響應由一個專門的Resource實例處理,你不需要操心這些方法的線程安全。
我們假設你已經讀過“第一步”(http://www.restlet.org/documentation/1.0/firstSteps),並且有了一些元件和應用的概念。

例子應用
讓我們開始描述我們的例子應用。一個Item列表用來管理創建,讀取,更新,和刪除活動像一個簡單的CRUD應用。一個Item是以名字和描述的簡單特徵。在簡短的分析後,我們定義了兩個資源:
 Items Resource等於所有可用Item的集合。
 Item Resource等於一個單獨的item。
現在,讓我們定義Resource URIs。假設我們的應用運行在本機“localhost”並且監聽8182端口:
 http://localhost:8182/firstResource/items:“items”Resource URI。
 http://localhost:8182/firstResource/items/{itemName}:“item”Resource URI,每個{itemName}代表一個Item的名字。
下一步,該定義允許訪問每個Resource的方法列表。
 “items”Resource響應GET請求並以一個XML文檔展示當前註冊的Item列表。另外,Resource支持通過POST請求創建新的Item。提交的實體包含新Item的名字和描述並接收格式化的Web表單。如果Resource成功創建新Item,它返回一個“Success - resource created”狀態(HTTP 201狀態代碼)並且告訴客戶端新Resource在哪裏可以找到(HTTP "Location" header)。否則,它返回一個“Client error”狀態(HTTP 404狀態代碼)和一個簡單的錯誤信息。
 “item”Resource響應GET請求並以一個XML文檔來展示該Resource的名字和描述。通過PUT和DELETE請求也能更新和刪除Resource。
在描述兩個Resource對象前,先編寫你的應用。爲了簡化,註冊的Item列表做爲應用的一個屬性簡單地保存到內存裏,並不保存到一個真實的數據庫。不管怎樣,我們假設你想邀請你的朋友們同時測試這個應用。因爲我們只有一個“FirstResourceApplication”實例在運行,所以不得不考慮線程安全。這也就是爲什麼你會發現Map對象Items是不變的,是一個ConcurrentHashMap對象的實例。

package firstResource;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Restlet;
import org.restlet.Router;
public class FirstResourceApplication extends Application {
/** The list of items is persisted in memory. */
private final Map<String, Item> items;
public FirstResourceApplication(Context parentContext) {
super(parentContext);
// We make sure that this attribute will support concurrent access.
items = new ConcurrentHashMap<String, Item>();
}
/**
* Creates a root Restlet that will receive all incoming calls.
*/
@Override
public synchronized Restlet createRoot() {
// Create a router Restlet that defines routes.
Router router = new Router(getContext());
// Defines a route for the resource "list of items"
router.attach("/items", ItemsResource.class);
// Defines a route for the resource "item"
router.attach("/items/{itemName}", ItemResource.class);
return router;
}
/**
* Returns the list of registered items.
* @return the list of registered items.
*/
public Map<String, Item> getItems() {
return items;
}
}


實現Items Resource
讓我們開始編寫Items Resource。如上文所示,允許GET和POST請求。POST請求支持通過“post(Representation)”方法確定你如何處理消息實體。此外,資源通過“allowPost”方法支持POST。缺省情況下,資源是不可更改的,拒絕POST、PUT和DELETE方法並返回“Method not allowed”狀態(HTTP 405狀態代碼)。同樣,GET響應支持通過“represent(Variant)”方法確定你如何依照特定的Variant生成實體。假設,我們只生成“text/xml”形式。

import java.io.IOException;
import java.util.Collection;
import org.restlet.Context;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.StringRepresentation;
import org.restlet.resource.Variant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Resource that manages a list of items.
*
*/
public class ItemsResource extends BaseResource {
/** List of items. */
Collection<Item> items;
public ItemsResource(Context context, Request request, Response response) {
super(context, request, response);
// Get the items directly from the "persistence layer".
items = getItems().values();
// Declare the kind of representations supported by this resource.
getVariants().add(new Variant(MediaType.TEXT_XML));
}
@Override
public boolean allowPost() {
return true;
}
/**
* Returns a listing of all registered items.
*/
@Override
public Representation getRepresentation(Variant variant) {
// Generate the right representation according to its media type.
if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
try {
DomRepresentation representation = new DomRepresentation(
MediaType.TEXT_XML);
// Generate a DOM document representing the list of
// items.
Document d = representation.getDocument();
Element r = d.createElement("items");
d.appendChild(r);
for (Item item : items) {
Element eltItem = d.createElement("item");
Element eltName = d.createElement("name");
eltName.appendChild(d.createTextNode(item.getName()));
eltItem.appendChild(eltName);
Element eltDescription = d.createElement("description");
eltDescription.appendChild(d.createTextNode(item
.getDescription()));
eltItem.appendChild(eltDescription);
r.appendChild(eltItem);
}
d.normalizeDocument();
// Returns the XML representation of this document.
return representation;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Handle POST requests: create a new item.
*/
@Override
public void post(Representation entity) {
// Parse the given representation and retrieve pairs of
// "name=value" tokens.
Form form = new Form(entity);
String itemName = form.getFirstValue("name");
String itemDescription = form.getFirstValue("description");
// Check that the item is not already registered.
if (getItems().containsKey(itemName)) {
generateErrorRepresentation(
"Item " + itemName + " already exists.", "1", getResponse());
} else {
// Register the new item
getItems().put(itemName, new Item(itemName, itemDescription));
// Set the response's status and entity
getResponse().setStatus(Status.SUCCESS_CREATED);
Representation rep = new StringRepresentation("Item created",
MediaType.TEXT_PLAIN);
// Indicates where is located the new resource.
rep.setIdentifier(getRequest().getResourceRef().getIdentifier()
+ "/" + itemName);
getResponse().setEntity(rep);
}
}
/**
* Generate an XML representation of an error response.
*
* @param errorMessage
* the error message.
* @param errorCode
* the error code.
*/
private void generateErrorRepresentation(String errorMessage,
String errorCode, Response response) {
// This is an error
response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
try {
// Generate the output representation
DomRepresentation representation = new DomRepresentation(
MediaType.TEXT_XML);
Document d = representation.getDocument();
Element eltError = d.createElement("error");
Element eltCode = d.createElement("code");
eltCode.appendChild(d.createTextNode(errorCode));
eltError.appendChild(eltCode);
Element eltMessage = d.createElement("message");
eltMessage.appendChild(d.createTextNode(errorMessage));
eltError.appendChild(eltMessage);
response.setEntity(new DomRepresentation(MediaType.TEXT_XML, d));
} catch (IOException e) {
e.printStackTrace();
}
}
}


實現Item Resource
讓我們繼續編寫Item Resource。如上文所示,允許GET,PUT和DELETE請求。PUT請求支持通過“put(Representation)”方法確定你如何處理消息實體。
同樣,DELETE請求支持執行“delete()”方法。

import java.io.IOException;
import org.restlet.Context;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.data.Status;
import org.restlet.resource.DomRepresentation;
import org.restlet.resource.Representation;
import org.restlet.resource.Variant;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class ItemResource extends BaseResource {
/** The underlying Item object. */
Item item;
/** The sequence of characters that identifies the resource. */
String itemName;
public ItemResource(Context context, Request request, Response response) {
super(context, request, response);
// Get the "itemName" attribute value taken from the URI template
// /items/{itemName}.
this.itemName = (String) getRequest().getAttributes().get("itemName");
// Get the item directly from the "persistence layer".
this.item = getItems().get(itemName);
if (this.item != null) {
// Define the supported variant.
getVariants().add(new Variant(MediaType.TEXT_XML));
}
}
/**
* This resource supports DELETE requests.
*/
@Override
public boolean allowDelete() {
return true;
}
/**
* This resource supports PUT requests.
*/
@Override
public boolean allowPut() {
return true;
}
/**
* Handle DELETE requests.
*/
@Override
public void delete() {
if (item != null) {
// Remove the item from the list.
getItems().remove(item.getName());
}
// Tells the client that the request has been successfully fulfilled.
getResponse().setStatus(Status.SUCCESS_NO_CONTENT);
}
@Override
public Representation getRepresentation(Variant variant) {
if (MediaType.TEXT_XML.equals(variant.getMediaType())) {
// Generate the XML representation of this resource.
try {
// Generate a DOM document representing the item.
DomRepresentation representation = new DomRepresentation(
MediaType.TEXT_XML);
Document d = representation.getDocument();
Element eltItem = d.createElement("item");
d.appendChild(eltItem);
Element eltName = d.createElement("name");
eltName.appendChild(d.createTextNode(item.getName()));
eltItem.appendChild(eltName);
Element eltDescription = d.createElement("description");
eltDescription.appendChild(d.createTextNode(item
.getDescription()));
eltItem.appendChild(eltDescription);
d.normalizeDocument();
// Returns the XML representation of this document.
return representation;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* Handle PUT requests.
*/
@Override
public void put(Representation entity) {
// Tells if the item is to be created of not.
boolean creation = (item == null);
// The PUT request updates or creates the resource.
if (item == null) {
item = new Item(itemName);
}
// Update the description.
Form form = new Form(entity);
item.setDescription(form.getFirstValue("description"));
// Update the item in the list.
getItems().put(item.getName(), item);
if (creation) {
getResponse().setStatus(Status.SUCCESS_CREATED);
} else {
getResponse().setStatus(Status.SUCCESS_OK);
}
}
}


實現Base Resource
由於我們的Resource都需要訪問“FirstResourceApplication”實例中保存Item的Map,這些方法因此被放入父類“BaseResource”。

import java.util.Map;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Resource;
/**
* Base resource class that supports common behaviours or attributes shared by
* all resources.
*
*/
public abstract class BaseResource extends Resource {
public BaseResource(Context context, Request request, Response response) {
super(context, request, response);
}
/**
* Returns the map of items managed by this application.
*
* @return the map of items managed by this application.
*/
protected Map<String, Item> getItems() {
return ((FirstResourceApplication) getContext().getAttributes().get(
Application.KEY)).getItems();
}
}


運行應用
請查閱“第一步”(http://www.restlet.org/documentation/1.0/firstSteps)的相關章節學習如何在一個Servlet容器中運行應用或作爲一個單獨應用執行。

客戶端應用
一旦我們的應用在一個Servlet容器或作爲一個單獨應用開始運行,我們建議你通過一個簡單的客戶端應用測試我們的Resource。它簡單地創建、讀取、更新和刪除一個Item Resource並在每一個操作執行時打印標準輸出結果。
客戶端應用作爲單獨Java應用運行的時候需要如下的Jar包:
 org.restlet.jar
 com.noelios.restlet.jar
 com.noelios.restlet.ext.httpclient.jar
 org.apache.commons.httpclient.jar

import java.io.IOException;
import org.restlet.Client;
import org.restlet.data.Form;
import org.restlet.data.Protocol;
import org.restlet.data.Reference;
import org.restlet.data.Response;
import org.restlet.resource.Representation;
public class FirstResourceClientMain {
public static void main(String[] args) throws IOException {
// Define our Restlet HTTP client.
Client client = new Client(Protocol.HTTP);
// The URI of the resource "list of items".
Reference itemsUri = new Reference(
"http://localhost:8182/firstResource/items");
// Create a new item
Item item = new Item("item1", "this is an item.");
Reference itemUri = createItem(item, client, itemsUri);
if (itemUri != null) {
// Prints the representation of the newly created resource.
get(client, itemUri);
}
// Prints the list of registered items.
get(client, itemsUri);
// Update the item
item.setDescription("This is an other description");
updateItem(item, client, itemUri);
// Prints the list of registered items.
get(client, itemsUri);
// delete the item
deleteItem(client, itemUri);
// Print the list of registered items.
get(client, itemsUri);
}
/**
* Try to create a new item.
*
* @param item
* the new item.
* @param client
* the Restlet HTTP client.
* @param itemsUri
* where to POST the data.
* @return the Reference of the new resource if the creation succeeds,
* null otherwise.
*/
public static Reference createItem(Item item, Client client,
Reference itemsUri) {
// Gathering informations into a Web form.
Form form = new Form();
form.add("name", item.getName());
form.add("description", item.getDescription());
Representation rep = form.getWebRepresentation();
// Launch the request
Response response = client.post(itemsUri, rep);
if (response.getStatus().isSuccess()) {
if (response.isEntityAvailable()) {
try {
// Always consume the response's entity, if available.
response.getEntity().write(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
return response.getEntity().getIdentifier();
}
return null;
}
/**
* Prints the resource's representation.
*
* @param client
* client Restlet.
* @param reference
* the resource's URI
* @throws IOException
*/
public static void get(Client client, Reference reference)
throws IOException {
Response response = client.get(reference);
if (response.getStatus().isSuccess()) {
if (response.isEntityAvailable()) {
response.getEntity().write(System.out);
}
}
}
/**
* Try to update an item.
*
* @param item
* the item.
* @param client
* the Restlet HTTP client.
* @param itemUri
* the resource's URI.
*/
public static boolean updateItem(Item item, Client client,
Reference itemUri) {
// Gathering informations into a Web form.
Form form = new Form();
form.add("name", item.getName());
form.add("description", item.getDescription());
Representation rep = form.getWebRepresentation();
// Launch the request
Response response = client.put(itemUri, rep);
if (response.isEntityAvailable()) {
try {
// Always consume the response's entity, if available.
response.getEntity().write(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
return response.getStatus().isSuccess();
}
/**
* Try to delete an item.
*
* @param client
* the Restlet HTTP client.
* @param itemUri
* the resource's URI.
*/
public static boolean deleteItem(Client client, Reference itemUri) {
// Launch the request
Response response = client.delete(itemUri);
if (response.isEntityAvailable()) {
try {
// Always consume the response's entity, if available.
response.getEntity().write(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
return response.getStatus().isSuccess();
}
}

你可以從http://www.restlet.org/documentation/1.0/examples/firstResource/sources.zip下載這個例子應用的源文件。

結尾
這篇文章說明了使用Restlet框架的重要方法。當思考你的Resource的時候,記住這些重要問題:
 我應該如何識別我的Resource?
 它們能夠生成哪些表現?
 它們有哪些處理方法?
 我需要把同級Resource的哪些行爲和屬性共享?
我們希望你喜歡這些簡單步驟並且我們現在鼓勵你開始深入Restlet Tutorial。

備註:
感謝Tim Peierls反饋線程安全方面的考慮。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章