The central API for accessing RESTful services in Spring 3 is RestTemplate. It is like any other template mechanism provided by Spring for the client side access. The default implementation uses the java.net package for creating HTTP requests. RestTemplate can be backed by Commons HttpClient using ClientHttpRequestFactory. Using Commons HttpClient as the backend implementation supports basic authentication in the requests. The javadocs were the single source of information for Spring3 REST support until M2, but you can find the latest reference documentation with M3 release here. Chapter 18 covers REST support in Spring 3. You can also read Arjen’s blog posts here and here which outlines the REST support in Spring 3 both on the server-side and client-side.

I was playing with the RestTemplate and was quite impressed with it as it comes handy for Spring developers. But, this implementation is no different than most other REST Client APIs. Long story short, this is yet another attempt to provide a non-standard Client for accessing RESTful resources. I am leaning more towards the need for a standard JAX-RS client API and I hope JAX-RS 2.0 fuels that interest. Lets explore with an example how Spring 3 provides native support for accessing RESTful resources using RestTemplate.

Twitter is one of the most widely used service which has a decent REST API with basic authentication.

Lets start with the POM dependencies. I had built the latest Spring3 M3 snapshot on my box, but you should be able to fetch them from Spring maven snapshots repository.

<repositories>
    <repository>
        <id>SpringSource Enterprise Bundle Repository - External Bundle Milestones</id>
        <url>http://repository.springsource.com/maven/bundles/milestone</url>
    </repository>
    <repository>
        <id>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</id>
        <url>http://repository.springsource.com/maven/bundles/release</url>
    </repository>
    <repository>
        <id>SpringSource Enterprise Bundle Repository - External Bundle Releases</id>
        <url>http://repository.springsource.com/maven/bundles/external</url>
    </repository>
</repositories>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
</build>
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>org.springframework.web</artifactId>
        <version>3.0.0.M3</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.ws</groupId>
        <artifactId>org.springframework.xml</artifactId>
        <version>1.5.5.A</version>
    </dependency>
    <dependency>
        <groupId>org.apache.log4j</groupId>
        <artifactId>com.springsource.org.apache.log4j</artifactId>
        <version>1.2.15</version>
    </dependency>
    <dependency>
        <groupId>commons-httpclient</groupId>
        <artifactId>commons-httpclient</artifactId>
        <version>3.1</version>
    </dependency>
</dependencies>

The Spring configuration defines the beans required for this sample.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oxm="http://www.springframework.org/schema/oxm"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">
    <bean id="twitterClient" class="spring3.restclient.TwitterClient">
        <constructor-arg ref="restTemplate"/>
        <constructor-arg ref="credentials"/>
    </bean>

    <bean id="httpClientParams" class="org.apache.commons.httpclient.params.HttpClientParams">
        <property name="authenticationPreemptive" value="true"/>
        <property name="connectionManagerClass"
                  value="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
    </bean>
    <bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
        <constructor-arg ref="httpClientParams"/>
    </bean>
    <bean id="credentials" class="org.apache.commons.httpclient.UsernamePasswordCredentials">
        <constructor-arg value="username"/>
        <constructor-arg value="password"/>
    </bean>
    <bean id="httpClientFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory">
        <constructor-arg ref="httpClient"/>
    </bean>

    <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <constructor-arg ref="httpClientFactory"/>

        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                    <property name="marshaller" ref="jaxbMarshaller"/>
                    <property name="unmarshaller" ref="jaxbMarshaller"/>
                </bean>
                <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
            </list>
        </property>
    </bean>

    <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="classesToBeBound">
            <list>
                <value>twitter.model.Statuses</value>
            </list>
        </property>
    </bean>
</beans>

MarshallingHttpMessageConverter is used to marshal/unmarshal JAXB beans. Statuses is a JAXB bean which is bound to the response returned by GET on the Twitter URI https://twitter.com/statuses/friends_timeline.xml. FormHttpMessageConverter is used to construct form parameters to POST on the Twitter URI https://twitter.com/statuses/update.xml.

Now, we will see the RestTemplate in action. You can see the code is just a one liner.

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.auth.AuthScope;
import org.springframework.http.client.CommonsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import org.springframework.util.MultiValueMap;
import org.springframework.util.LinkedMultiValueMap;
import twitter.model.Statuses;

import java.util.HashMap;
import java.util.Map;

public class TwitterClient {

    private final RestTemplate restTemplate;

    private final Credentials credentials;

    private static final String twitterGet = "https://twitter.com/statuses/friends_timeline.xml?count={count}";
    private static final String twitterPost = "https://twitter.com/statuses/update.xml";

    public TwitterClient(RestTemplate restTemplate, Credentials credentials) {
        this.restTemplate = restTemplate;
        this.credentials = credentials;
        CommonsClientHttpRequestFactory factory = (CommonsClientHttpRequestFactory) restTemplate.getRequestFactory();
        HttpClient client = factory.getHttpClient();
        client.getState().setCredentials(AuthScope.ANY, credentials);
    }

    public Statuses getStatuses() {
        Map<String, String> vars = new HashMap<String, String>();
        vars.put("count", "5");
        return restTemplate.getForObject(twitterGet, Statuses.class, vars);
    }

    public void setStatus(String status) {
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("status", status);
        restTemplate.postForLocation(twitterPost, map);
    }
}

The test code which posts the status to your Twitter account is shown below.

public class Spring3RestClientTest {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        TwitterClient twitter = applicationContext.getBean("twitterClient", TwitterClient.class);
        twitter.setStatus("Spring RestTemplate rocks!");
        Statuses statuses = twitter.getStatuses();
        for (Statuses.Status status : statuses.getStatus()) {
            System.out.println("Text : " + status.getText());
            System.out.println("User : " + status.getUser().getScreenName());
        }
    }
}

Here is an example output of the above test which lists recent five status messages from Twitter:
=====================================================
Text : Spring RestTemplate rocks!
User : aruld
Text : Just finished celebrating with the SpringSource & Hyperic teams at Tres Agaves. Really good to meet everyone! http://twitpic.com/4l1hb
User : cbeams
Text : 8 Java Puzzlers for JavaOne done. Wow. Soon we’ll scrape right through the bottom of the barrel.
User : gafter
Text : Disappointed my Maredsous 8 is all gone. I should’ve bought more.
User : dandiep
Text : Headed to San Francisco for a week of Core Spring training & looking forward to it!
User : cbeams
=====================================================

RestTemplate does serves its purpose, but it could be improved to support additional convenient methods for accessing URIs with query parameters. The other thing I noticed is about making HTTP POST with a name-value pair using postForLocation API. I could not find any details from the documentation so far. For instance, I would like to do a post to the URI http://twitter.com/statuses/update.xml with the form data “status=This is my current status” sent in the HTTP body. I am not sure whether this is possible with the current implementation. I will dig into this further and keep posted on what I find.

Update (5/4): Thanks to Arjen for clarifying the URITemplate use within query parameters and the FormHttpMessageConverter for using Form parameters. The blog has been updated to use these techniques. You could POST a twitter message just fine :)

Update (5/10): Updated the reference documentation to the official M3 build from SpringSource and updated the maven POM to use the M3 dependency.

Possibly Related Posts:


31 Responses to “RestTemplate, the Spring way of accessing RESTful services”

  • poutsma

    Hi,

    Thanks for your interest!

    You should be able to use query parameters with the RestTemplate as part of the URL, as URI templates, e.g. http://example.com/somewhere?foo={bar}. My blog post shows this for flickr.

    For submitting forms, you should be able to use postForLocation, and supply a org.springframework.util.MultiValueMap containing the form data. This uses the org.springframework.http.converter.FormHttpMessageConverter underneath.

  • Arul

    Hi Arjen,

    Thanks for your comment. I appreciate your clarification on query params and form params use in RestTemplate. I fixed the sample code and it worked like a charm.

    Spring RestTemplate rocks!

    -Arul

  • mahesh

    Hi Arul,

    I am trying to invoke a Restfull API but at the time of execution it is not able find the class org.springframework.util.MultiValueMap can you please help in this by providing information related to the jar file that I can use.

  • mahesh

    Hi Arul,

    I just solved the problem of org.springframework.util.MultiValueMap

    but now I am not able to find import twitter.model.Statuses;

    Can you give me some info related hoe to get twitter.model.Statuses; jar file

  • Arul

    Hi Mahesh,

    It is a simple bean generated from Twitter API schema using JAXB. You can find the source here.

    -Arul

  • KK

    Hi Aurl,

    Can you tell me (or point to some ref.) how to generate other Twitter objects using JAXB. I’m not finding any XML Schema references from Twitter site, as they show only incomplete XML samples and not the full possbile XML.

    Thanks a lot.

    Regards,
    KK.

  • Arul

    Hi KK,

    Unfortunately, Twitter does not expose the XML schemas. I had to reverse engineer from XML -> XSD -> JAXB.

    HTH,
    Arul

  • moqthiar

    Hi Arul,

    I am trying to execute a sample program using PostForLocation(), but it is giving 500 error.
    When I am using SoapUI3.0 I am getting the proper status in xml.Can you please help me in this.

    Is there any way to specify the ResponseType while using postForLocation method from client.

    Regards
    Moqthiar

  • Arul

    Hi Moqthiar,

    I am not sure why you are getting 500, unless there is an issue with your web server. You may want to check the root cause from RestClientException thrown by postForLocation API.

    If you could post your code, I can try looking into it.

    Cheers,
    Arul

  • Moqthiar

    Hi Arul,

    Thanks for your reply.

    I will place my question the other way. I am trying to post some data to the restful api very much similar to the code above by creating a map object.

    When i debug the code at the server end the control is entering to the post method that i intend to. After executing the logic it should return an xml/json response.

    I am using contentNegotiationViewResolver at the server end where the RequestMethod.GET methods are returning the correct response when iam prefixing the URL with .xml or .json to the getForObject() method with out any error.

    For the postForLocation() when iam prefixing the URL with either .xml or .json its not giving any error. But the return type object URI is null.
    When iam not prefixing the URL iam getting 500 exception.

    In either case the right method at server end is getting invoked.

    My queries are
    1. How to get the xml/json response returned by the POST method of restful api

    //Client Code

    RestTemplate template = new RestTemplate();
    List defaultConverters =
    new ArrayList(Arrays.asList(template.getMessageConverters()));
    defaultConverters.add(new org.springframework.http.converter.FormHttpMessageConverter());
    HttpMessageConverter[] converters = new HttpMessageConverter[defaultConverters.size()];
    template.setMessageConverters(defaultConverters.toArray(converters));

    //Posting the data
    MultiValueMap map = new LinkedMultiValueMap();
    map.add(“id”, “42″);
    URI uri = getRestTemplate().postForLocation(“http://192.168.3.64:8080/TestRestBundle/hello/mahesh/yamsani.json”,map);
    //Iam getting the output as URI is null
    if(uri == null){
    System.out.println(“UrI id null”);
    }

    //Server Code

    @RequestMapping(value=”hello/{firstName}/{lastName}”, method = RequestMethod.POST)
    public ModelAndView saveDetails(@RequestParam(“id”) int id,@PathVariable(“firstName”) String firstName, @PathVariable(“lastName”) String lastName){
    ModelAndView view = new ModelAndView();

    Status status = new Status();
    status.setId(id);
    status.setStatus(“Success”);
    view.setViewName(“status”);
    view.addObject(“model”,status);
    return view;
    }

    Thanks in advance

    Regards
    Moqthiar

  • Arul

    Hi Moqthiar,

    I have been quite busy for the past few weeks. I will probably have a chance to look into this over the weekend, in case if you have not figured out yet.

    Cheers,
    Arul

  • Moqthiar

    Hi Arul,

    It would be great if you have time to look into that. I too have not worked on this further b’coz of some other tasks.

    Regards
    Moqthiar

  • Arul

    Hi Moqthiar,

    I believe you are getting URI as null which is expected as per your code snippet. It seems your controller does not manage the URI space. URI management is critical to RESTful service design. It can be managed on the server side or on the client side or it could even be a hybrid approach based on the requirements.

    You may want to look into a simple Spring example where you could see the Location header set on the HTTP response inside the PostServlet. I bet, Spring has a better way to handle this on the Controller. Unfortunately, the literature in this area is *so-so*.

    I would suggest you to look into Jersey or any other JAX-RS implementation which is more focused on the RESTful service development and the RESTful concepts are easier to implement and test with.

    Hope this helps.

    Cheers,
    Arul

  • moqthiar

    Thanks Arul,

    Your example code snippet has helped me in finding the solution.

    In 3.0.0.M3 jars RestTemplate class doesnot provide the postForObject() method but after looking into sample code i updated all my jars to 3.0.0.M4 and i got my expected result

    Thanks again

    Moqthiar

  • Arul

    Hi Moqthiar,

    You are welcome. I am glad you were able to get this working.

    Thanks for posting back!

    Cheers,
    Arul

  • fray

    Hi Arul:
    I read your post and very helpful info. I am new to REST, and i am trying to use the resttemplate to do the GET (https://localhost:8080/rest/id/123), I got the SSL handshake, i read the blog of Arjen, he mentioned about using Commons HTTP. I am not sure how really it will work. I saw your blog have something similar but you use the commons-httpclient.jar .

    1. Do i need use the commons-httpclient.jar?
    2. If i use the resttemplate has the constructor expecting the interface(ClientHttpRequestFactory), how would i passing on the authentication info in here, like (user/password, certification key)..

    Greatly appreciated if you can help with my question.

    Thanks

  • fray

    Hi Arul
    I saw your post and doing the authentication using commons-https. I am very new to RESt using spring3 m4.

    1. I have a get like this from controller to service, in my service i have the resttemplate pass the https://localhost:8080/rest/ids/23456. I got the SSL handshake failure.
    i read on Arjen post about the resttemplate taking the interface as constructor. But not quite sure how to use it, can you give me some snippet or what are the approach i should take.

    Regards

  • Arul

    Hi Fray,

    The ClientHttpRequestFactory allows users to plugin either standard J2SE based facility for creating HTTP requests or using CommonsClient APIs. The SimpleClientHttpRequestFactory is very primitive when compared to the comprehensive CommonsClient backed CommonsClientHttpRequestFactory. If you need to perform authentication with your Restful resources, then you may want to use RestTemplate backed by CommonsClient.

    Please refer to the Spring configuration in the blog entry which does the magic of wiring HttpClient with RestTemplate using the ClientHttpRequestFactory. The credentials are configured as a bean in Spring config.

    Let me know if you have any specific questions.

    HTH,
    Arul

  • Fray

    Thank Arul for your direction. I follow your blog, My question is :

    1. Do I need to have the commons-httpclient.jar from org.apache.commons.httpclient in order to pass in the user/password?

    snippet from my applicationcontext.xml

    class=”…RestTemplate”>

    In my service
    public class MyService implements MyServiceI {
    private RestTemplate restTemplate;

    public String getData(String url,String id) {

    return getRestTemplate.getForObject(url,String.class,id);
    —-> so what do I do here in order to pass in the authentication info.
    }

    getter/setter for RESTtemplate
    }

    Very much thank you.
    Regards

    Fray

  • Arul

    Hi Fray,

    Yes, you would require commons-httpclient-3.1.jar for creating HTTP requests with basic auth.

    The following code would set the credentials for your requests (refer to the TwitterClient constructor) :
    CommonsClientHttpRequestFactory factory = (CommonsClientHttpRequestFactory) restTemplate.getRequestFactory();
    HttpClient client = factory.getHttpClient();
    client.getState().setCredentials(AuthScope.ANY, credentials);

    You need to inject a ‘httpClientFactory’ to the RestTemplate, in this case the client factory is backed by Commons HTTP Client.

    Let me know if this helps.

    Cheers,
    -Arul

  • Fray

    Hi again,
    Thnks for your help. I have email you via the Contact me, not sure if you got it.
    I have following your blog sample, how would you handle like passing all the cert key information including password, truststore, keystore more likely doing the SSL info using RestTemplate.

    If you got any ideas, please help.

  • Arul

    Hi Fray,

    I did receive your email. I did not had a chance to respond to your email yet.

    You may want to look at the SSL Guide for Commons HTTP Client. You need to configure the HttpClient from RestTemplate with an SSLSocketFactory.

    HTH,
    Arul

  • Fray

    Thanks Arul, that helps. I am looking into it .
    Greatly appreciated your direction and time to help out.

  • Arul

    Hi Fray,

    No problem.

    You may want to look at the Jersey client sample used to access a secured resource. This would give you an idea on how to setup TrustManager and KeyManager on the client.

    Cheers,
    Arul

  • Fray

    Arul
    I got the SSL work, thanks to your links and direction.

    Once more question: I format the return from Rest Servie as a json. In spring 3M4, there is no coverter for Json, I saw you using the FormHttpMessageConverter. How would you handle when stuff return are in JSON format

    again greatly appreciated

  • Fray

    Thanks…wow it is very helpful blog for SSL & resttemplate stuff..

    Very helpful info.

  • mack

    i got this error

    Exception in thread “main” org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:69)
    at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:486)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:443)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:401)
    at org.springframework.web.client.RestTemplate.postForLocation(RestTemplate.java:257)
    at twitter.TwitterClient.setStatus(TwitterClient.java:40)
    at twitter.Spring3RestClientTest.main(Spring3RestClientTest.java:13)

  • Hari

    Hi,

    Getting the below error while executing the above code.

    POST request for “https://twitter.com/statuses/update.xml” resulted in 401 (Unauthorized); invoking error handler
    Exception in thread “main” org.springframework.web.client.HttpClientErrorException: 401 Unauthorized

    Please suggest how twitter credentials can be passed to the code?

    Thanks in advance very much!

  • Arul

    Hi Mack, Hari,

    I believe you are not setting the correct Twitter credentials. The credentials are set in line numbers 19 and 20 in applicationContext.xml. I have highlighted them above. This should fix the 401 error you are seeing.

    -Arul