RESTing with Roo - adding Content Negotiation and REST in two easy steps (well, kinda)
Thursday, July 1, 2010 at 5:12AM Ok, here's another stream-of-consciousness post on REST support from a Roo application. I'm working on various research for our [PLUG] SpringSource Core Spring training classes, always attempting to get better examples so I can do more than just show the slides and labs, and I decided to use Roo to build myself a REST server.
Easy, I thought... Roo has RESTful URLs, and so I just have to set up the right configuration.
Kind of.
The basic steps to REST-enable a web app in Spring MVC using the built-in REST support are:
- Install the proper JARs for your Marshallers
- Install the ContentNegotiatingViewResolver
I had a bit of a hiccup figuring this out. Thanks to a great pair of threads on SpringSource's forums, here and here, I realized I had configured a few things wrong.
Setting up the example
Here's my Spring Roo log file, which you can cut and paste into a text file and then do a "roo script filename" command to set up for you.
project --topLevelPackage rest.demo --projectName spring-mvc-rest-demo persistence setup --provider HIBERNATE --database HYPERSONIC_PERSISTENT entity --class ~.db.Customer field string firstname field string lastname field date --fieldName dob --type java.util.Date field number --fieldName discount --type java.math.BigDecimal --decimalMin 0 --decimalMax 1.0 controller scaffold --entity rest.demo.db.Customer --class rest.demo.web.CustomerController
Once you do that, now you're ready for some fun...
The additional dependencies for your Maven pom.xml file (you'll need to add these to the main dependencies tag section at the end...) (for XML conversion, we're using Castor, which auto-generates XML based on the reflection of JavaBean properties on your bean, and for JSON we're using the Jaskson JSON library. For this to work, you have to install spring-oxm, which isn't installed in Roo by default - I SMELL PLUGIN... RACE YA!).
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor-xml</artifactId>
<version>1.3.1</version>
</dependency>
Now, the Spring webmvc-config.xml file changes. This file is located in src/main/webapp, under the WEB-INF/spring directory, of course. Add this to the end of the file before the closing beans tag...
<bean id="htmlMediaType" class="org.springframework.http.MediaType">
<constructor-arg value="text" />
<constructor-arg value="html" />
</bean>
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name="order" value="0" />
<property name="defaultContentType">
<ref bean="htmlMediaType" />
</property>
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
<property name="defaultViews">
<list>
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
<property name="marshaller">
<bean class="org.springframework.oxm.castor.CastorMarshaller" />
</property>
</bean>
</list>
</property>
<property name="viewResolvers">
<ref bean="tilesViewResolver" />
</property>
</bean>
Next, in the same file, add the order property, setting it to 1, to the instance of the AjaxUrlBasedViewResolver so that it looks roughly like this :
<bean class="org.springframework.js.ajax.AjaxUrlBasedViewResolver"
id="tilesViewResolver">
<property name="order" value="1" />
<property name="viewClass"
value="org.springframework.web.servlet.view.tiles2.TilesView" />
</bean>
What you're seeing at work here in this setup is the configuration of a default media type of text/html, and a configuration of a first-level view resolver that does content negotiation first. You'll see the order setting was made to make the AjaxBasedViewResolver go first, which threw me, but that's an area for more research as I start to dig deeper into the interplay between the various view resolvers... Generally you want the JSP file-based resource view resolver to go last, as it is greedy, but Tiles changes that for us a bit. I am not 100% sure everything works as advertised - for example I need to do some AJAX testing to make sure those calls work. But for getting REST against a webapp, well, there you have it.
Testing it!
Well, that wasn't so much configuration, was it? I'm experimenting with REST using the Groovy
RESTClient library at the moment (I'm going to use Spring REST Template in JUnit4 for the class, but also wanted
something I could script up at a moment's notice and show them how REST works without waiting
for those compile cycles...) BTW, I'm on Groovy 1.7.2 (haven't yet upgraded to 1.7.3) and the @Grab line uses Groovy's GRAPE artifact manager to install a grape from the ole' vine (puts it in ~/.groovy/grape/grapes or something)... WICKED COOL. Yes, I used my blog to say WICKED COOL. You can't stop me!
@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.5.0' ) import groovyx.net.http.RESTClient def customers = new RESTClient( 'http://localhost:8008/demo/') def results = customers.get (path : 'customers', headers: [Accept : 'application/json', "Accept-Encoding" : 'gzip,deflate']) println results.data
Switch from JSON to XML, using application/xml as the Accept header, and you'll see it automatically respond with the appropriate datatype. Great! Now, if you use XML, change from that println statement to this:
results.data.customer.each { c ->
println "customer: $c.id, $c.firstname $c.lastname"
}
And if you want to test it on the browser, and see it do content negotation by file extension
instead, just hit: http://localhost:8080/spring-mvc-rest-demo/customers.xml or http://localhost:8080/spring-mvc-rest-demo/customers.json
to see the REST data, and http://localhost:8080/spring-mvc-rest-demo/customers to see the regular
tiles views.
Enjoy...
Oh, and while I'm shilling for my training group at Chariot, please visit our course calendar if you are interested in our fall lineup - Hibernate with Spring, Spring Enterprise Integration, Core Spring, or Maven Intro and Advanced training. Thanks, and I'll refrain from using a strange chamois in any of my in-line advertisements...
Ken Rimple
Interestingly, I found this when I was testing the RESTTemplate, a Roo Addon to support content negotiation!
Ken Rimple |
9 Comments |