Building and testing the service
Now that you know how you'll work with XML and already have a data layer to use, it's time to continue building your RESTful application with Restlets and do some test preparation.
现在你知道了如何使用XML和已经有了可以直接使用的数据层级,现在要开始构建RESTful应用了,先做点实践的准备。
The Races service
Recall that Acme Racing would like its service to enable clients to view existing races as well as create new ones.You've already outlined the RESTful URI that'll support this behavior:/race.
Via the Router class in the RaceApplication class, you linked this URI to the RacesResource class. You already know that you must implement three methods:
通过RaceApplication类,你将URI与RacesResource类链接起来,你需要实现下面三个方法。
*getRepresentation()
*allowPost()
*post()
Accordingly,create a class called RacesResource and ensure that it extends org.restlet.resource.Resource.Also,implement a three-parameter constructor,as shown in Listing13:
因此,创建RacesResource类并确定他继承org.restlet.resource.Resource。实现他拥有三个参数的构造方法如下:
public class RacesResource extends Resource { public RacesResource(Context context, Request request, Response response) { super(context, request, response); } }
Restlets must be instructed how to communicate resource representations properly.Because XML will serve as the resources format,you must direct your restlet by adding an XML variant type.Variants,in Restlets,represent a format for Resources.The base class,Resource,contains a getVariants() method that facilitates adding various Variant types.Accordingly,add the line in Listing14 to our constructor:
Restlets必须被指示怎么联系适当的resource。因为XML被当做resource的格式。你必须通过XML的variant类型来指示你的restlet。Variants,在Restlets中,代表Resource的格式。Resource基础类中,有一个getVariants()方法来方便添加各种Variant。如下:
this.getVariants().add(new Variant(MediaType.TEXT_XML));
The Restlet framework supports a wide variety of media types,including images and video.
Handling GET requests
Now it's time to implement the easiest behavior of the class: handling a GET request. Override the getRepresentation() method as shown in Listing 15:
下面是最简单的get请求的实现
public Representation getRepresentation(Variant variant) { return null; }
As you can see, this method returns a Representation type, of which there are multiple implementations. One implementation ― aptly dubbed StringRepresentation ― represents strings and will suffice for your needs.
As you know, you already have a legacy domain model that supports working with the database. It also turns out that someone has already written a utility class, called RaceReporter, that transforms domain objects into XML documents. This class's racesToXml() method takes a collection of Race instances and returns a String representing an XML document looking something like Listing 16:
如你所见,这个方法返回Representation 类型,这个类型有多种实现。一种实现-贴切的被称为StringRepresentation-代表会根据你的需要返回字符串。
如你所知的,你已经有了一个遗留的域对象支持数据库。他表明了有人已经写了一个有效的类,名叫RaceReporter,来讲域对象转化为XML文件。这个类的racesToXml方法返回的XML如下:
<acme-races> <races> <race name="Leesburg 5K" date="2008-05-12" distance="3.1" id="5"> <uri>/races/5</uri> <description/> </race> <race name="Leesburg 10K" date="2008-07-30" distance="6.2" id="6"> <uri>/races/6</uri> <description/> </race> </races> </acme-races>
In fact,this XML document is an example of what your RESTful Web service will return when the /race URI is invoked with a GET request.
事实上,这个XML是你的RESTful Web服务器将对/race URI GET请求返回的一个例子。
Therefore,your job is to link the retrieval of all race instances in the underlying data store; in fact ,at this point,you can already write a test.
Testing the service
Using the Restlet framework,you can construct a client instance and have it invoke your RESTful Web service.Moreover,you can leverage XMLUnit to verify that the service's output is some known XML document. Last, but not least, you can also use DbUnit (see Resources) to put the underlying database into a known state (so you can always get back the same XML
Using JUnit 4, you can create two fixtures that properly initialize XMLUnit and DbUnit, as shown in Listing 17:
@Before public void setUpXMLUnit() { XMLUnit.setControlParser( "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit.setTestParser( "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl"); XMLUnit.setSAXParserFactory( "org.apache.xerces.jaxp.SAXParserFactoryImpl"); XMLUnit.setIgnoreWhitespace(true); } @Before public void setUpDbUnit() throws Exception { Class.forName("org.hsqldb.jdbcDriver"); IDatabaseConnection conn = new DatabaseConnection( getConnection("jdbc:hsqldb:hsql://127.0.0.1", "sa", "")); IDataSet data = new FlatXmlDataSet(new File("etc/database/race-db.xml")); try { DatabaseOperation.CLEAN_INSERT.execute(conn, data); } finally { conn.close(); } }
In the setUpDbUnit method, an XML representation of the database is inserted into the database via the CLEAN_INSERT command. This XML file effectively inserts six different races. Therefore, the response to a GET will be an XML document with six races.
Next, you can create a test case that invokes an HTTP GET on the /race URI, obtains the response XML, and compares it to a control XML file using XMLUnit's Diff class, as demonstrated in Listing 18:
@Test public void getRaces() throws Exception { Client client = new Client(Protocol.HTTP); Response response = client.get("http://localhost:8080/racerrest/race/"); Diff diff = new Diff(new FileReader( new File("./etc/control-xml/control-web-races.xml")), new StringReader(response.getEntity().getText())); assertTrue(diff.toString(), diff.identical()); }