-
Notifications
You must be signed in to change notification settings - Fork 66
Tutorial: SpringMVC
This page is currently out of date. We are working on an updated tutorial. - 10/31/2012
Suppose you have an existing User domain object, that you would like to expose as a REST resource:
public class User
{
private long _id;
private String _name;
private Set<Artist> _favoriteArtists;
public long getId()
{
return _id;
}
public String getName()
{
return _name
}
public Set<Artist> getFavoriteArtists()
{
return _favoriteArtists;
}
}
Import the relevant Yoga projects.
Add the following to your pom.xml, and define yoga.version to the current number:
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>yoga-core</artifactId>
<version>${yoga.version}</version>
</dependency>
<dependency>
<groupId>org.skyscreamer</groupId>
<artifactId>yoga-springmvc</artifactId>
<version>${yoga.version}</version>
</dependency>
Make sure your SpringMVC controller supports RESTful URLs. In this example, we are supporting GET requests to /user
(to retrieve all users) and /user/{id}
(to retrieve a single user by id number)
@Controller
@RequestMapping("/user")
public class UserController extends AbstractController<User>
{
@RequestMapping
public List<User> getUsers()
{
return _userService.findAllUsers();
}
@RequestMapping("/{id}")
public User getUser( @PathVariable long id )
{
return _userService.findUser( id );
}
}
Next, register our supplied custom ModelAndViewResolver with your Spring MVC configuration. This is easy if you are using annotation configuration for your SpringMVC controllers (Spring 3.0 required):
<bean name="handlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customModelAndViewResolvers">
<list>
<bean class="org.skyscreamer.yoga.springmvc.view.YogaModelAndViewResolver" p:viewResolver-ref="viewResolver"/>
</list>
</property>
</bean>
<bean name="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"
p:order="1" p:ignoreAcceptHeader="true">
<property name="mediaTypes">
<map>
<entry key="json" value="application/json"/>
<entry key="xml" value="application/xml"/>
<entry key="xhtml" value="text/html"/>
</map>
</property>
<property name="defaultViews">
<list>
<bean class="org.skyscreamer.yoga.springmvc.view.JsonSelectorView"/>
<bean class="org.skyscreamer.yoga.springmvc.view.XmlSelectorView"/>
<bean class="org.skyscreamer.yoga.springmvc.view.XhtmlSelectorView"/>
</list>
</property>
</bean>
You can see that we have elected to support rendering your REST resources in JSON, XML, or XHTML response format. Of course, you can remove any formats that you don't wish to support from the viewResolver configuration.
Finally, in your application context register the ResultTraverser, which will navigate your nested object graph to return results that match what is requested by the Selector:
<bean id="resultTraverser" class="org.skyscreamer.yoga.mapper.ResultTraverser">
<property name="enrichers">
<list>
<bean class="org.skyscreamer.yoga.mapper.enrich.HrefEnricher"/>
<bean class="org.skyscreamer.yoga.mapper.enrich.SelectorBuilderEnricher"/>
<bean class="org.skyscreamer.yoga.mapper.enrich.NavigationLinksEnricher"/>
<bean class="org.skyscreamer.yoga.mapper.enrich.ModelDefinitionBuilder"/>
<bean class="org.skyscreamer.yoga.mapper.enrich.MetadataLinkEnricher" p:metaDataService-ref="metaDataService"/>
</list>
</property>
</bean>
The enrichers that are injected into the ResultTraverser implement Yoga's Enricher interface, and are used to customize the output format of the response. For example, you can control exactly how HREF or navigation links are displayed in the response.
Start your server. You can now issue a browser request to your controller, passing in selector
as a request parameter to determine what fields of the User object will be rendered in the response:
/user.json?selector=:(id,name)
Check out the other supported formats:
/user.xml?selector=:(id,name)
/user.xhtml?selector=:(id,name)
Notice that the User object has a property named favoriteArtists, which is a complex property type (set of Artist objects). If the Artist object has properties to render, you can name them in a nested selector. This request retrieves data for the user with ID#1, along with some information about his favorite artists:
/user/1.json?selector=:(id,name,favoriteArtists:(name,currentRecordLabel))
As you can imagine, these selector strings can get very verbose for rich resources or complex object graphs. To simplify the selectors, you can designate certain properties of your REST resource as Core properties. Core properties will always be returned when the resource is rendered, and do not need to be named in the Selector.
One way to designate a property as a Core property is to use the @Core annotation that is supplied with the Yoga libraries. Here, we apply the annotation to two getter methods of the User class:
@Core
public long getId()
{
return _id;
}
@Core
public String getName()
{
return _name
}
Now, the following request will render the id and name properties of the User resource.
/user/1.json
And this request will render the id and name fields of the User resource as well as the name and currentRecordLabel fields of all of the user's favorite artists.
/user/1.json?selector=:(favoriteArtists:(name,currentRecordLabel))
When deploying a REST interface, you will want the resources that you return to be addressable through a clean URI. You can use the class annotation @URITemplate to tell Yoga how to construct the URI for an instance of your resource. Here's an example:
@URITemplate("/user/{id}")
public class User
{
private long _id;
......
Here, the brackets around id tell Yoga to substitute the id field of the user instance in that part of the URI. So if the controller was returning the User object that had an id of 5, then the JSON response would include the following href field:
"href":"/user/5.json"
If you do not specify a URI template through the class annotation (or through a FieldPopulator for the resource), then no href link will be returned as part of the response.
This is a great example for getting started quickly, and seeing what you can do with selectors in Yoga. We've been operating under the naive (but fun!) assumption that all of the data you would like to expose to REST requests is encapsulated in properties on your model objects, and that you want to expose all of your model data to anyone who asks.
To learn how to perform a more controlled deployment of Yoga, proceed to Configuring Resources with FieldPopulators