What is REST in Spring 3?
For a great introduction, please see the following links:
- REST in Spring 3: @MVC
- REST in Spring 3: RestTemplate
What is RestTemplate?
The central class for client-side HTTP access. It simplifies communication with HTTP servers, and enforces RESTful principles. It handles HTTP connections, leaving application code to provide URLs (with possible template variables) and extract results.
Source: Spring 3 Reference: RestTemplate
Development
Our client application is unique because it doesn't need a Service layer. We only need the Domain and the Controller layers. We also need a couple of JSP pages to display the records.Domain Layer
We'll be declaring two domain objects: Person and PersonList. These are the same class declarations we had in the REST provider application.Person.java
package org.krams.tutorial.domain; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="person") public class Person { private Long id; private String firstName; private String lastName; private Double money; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } }Person is a simple POJO consisting of three properties. Notice we've annotated the class name with @XmlRootElement(name="person"). Its purpose is to help JAXB (the one responsible for marshalling/unmarshalling to XML) determine the root of our POJO.
PersonList.java
package org.krams.tutorial.domain; import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name="persons") public class PersonList { @XmlElement(required = true) public List<person> data; @XmlElement(required = false) public List<person> getData() { return data; } public void setData(List<person> data) { this.data = data; } }PersonList is another simple POJO. It's purpose is to wrap a list of Person objects.
Ideally we should be able to return a List
To resolve this issue with JAXB, we wrap our List
Controller Layer
The controller layer is the most important section of the client application because here's where we communicate with the REST provider. It's also responsible for displaying the JSP pages.ProviderController.java
package org.krams.tutorial.controller; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; import org.krams.tutorial.domain.Person; import org.krams.tutorial.domain.PersonList; import org.krams.tutorial.util.Writer; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.client.RestTemplate; import javax.servlet.http.HttpServletResponse; /** * REST service client */ @Controller public class RestClientController { protected static Logger logger = Logger.getLogger("controller"); private RestTemplate restTemplate = new RestTemplate(); /** * Retrieves an image from the REST provider * and writes the response to an output stream. */ @RequestMapping(value = "/getphoto", method = RequestMethod.GET) public void getPhoto(@RequestParam("id") Long id, HttpServletResponse response) { logger.debug("Retrieve photo with id: " + id); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.IMAGE_JPEG); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<String> entity = new HttpEntity<String>(headers); // Send the request as GET ResponseEntity<byte[]> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, byte[].class, id); // Display the image Writer.write(response, result.getBody()); } /** * Retrieves all records from the REST provider * and displays the records in a JSP page */ @RequestMapping(value = "/getall", method = RequestMethod.GET) public String getAll(Model model) { logger.debug("Retrieve all persons"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<PersonList> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/persons", HttpMethod.GET, entity, PersonList.class); // Add to model model.addAttribute("persons", result.getBody().getData()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/personspage.jsp return "personspage"; } /** * Retrieves a single record from the REST provider * and displays the result in a JSP page */ @RequestMapping(value = "/get", method = RequestMethod.GET) public String getPerson(@RequestParam("id") Long id, Model model) { logger.debug("Retrieve person with id: " + id); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id); // Add to model model.addAttribute("person", result.getBody()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/getpage.jsp return "getpage"; } /** * Retrieves the JSP add page */ @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAddPage(Model model) { logger.debug("Received request to show add page"); // Create new Person and add to model // This is the formBackingOBject model.addAttribute("personAttribute", new Person()); // This will resolve to /WEB-INF/jsp/addpage.jsp return "addpage"; } /** * Sends a new record to the REST provider * based on the information submitted from the JSP add page. */ @RequestMapping(value = "/add", method = RequestMethod.POST) public String addPerson(@ModelAttribute("personAttribute") Person person, Model model) { logger.debug("Add new person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers); // Send the request as POST try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person", HttpMethod.POST, entity, Person.class); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; } /** * Retrieves the JSP update page */ @RequestMapping(value = "/update", method = RequestMethod.GET) public String getUpdatePage(@RequestParam(value="id", required=true) Integer id, Model model) { logger.debug("Received request to show edit page"); // Retrieve existing Person and add to model // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id); // Add to model model.addAttribute("personAttribute", result.getBody()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/updatepage.jsp return "updatepage"; } /** * Sends an update request to the REST provider * based on the information submitted from the JSP update page. */ @RequestMapping(value = "/update", method = RequestMethod.POST) public String updatePerson(@ModelAttribute("personAttribute") Person person, @RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Update existing person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers); // Send the request as PUT ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.PUT, entity, String.class, id); // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; } /** * Sends a delete request to the REST provider */ @RequestMapping(value = "/delete", method = RequestMethod.GET) public String deletePerson(@RequestParam("id") Long id, Model model) { logger.debug("Delete existing person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<String> entity = new HttpEntity<String>(headers); // Send the request as DELETE ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.DELETE, entity, String.class, id); // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; } }Notice our controller is our typical Spring MVC 3 controller.
Let's dissect our controller and analyze each methods.
getPhoto() method
The getPhoto() method retrieves an image as a byte array from the REST provider. It displays the image by writing it to the output stream using a helper class: Writer/** * Retrieves an image from the REST provider * and writes the response to an output stream. */ @RequestMapping(value = "/getphoto", method = RequestMethod.GET) public void getPhoto(@RequestParam("id") Long id, HttpServletResponse response) { logger.debug("Retrieve photo with id: " + id); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.IMAGE_JPEG); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<String> entity = new HttpEntity<String>(headers); // Send the request as GET ResponseEntity<byte[]> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, byte[].class, id); // Display the image Writer.write(response, result.getBody()); }
Writer.java
package org.krams.tutorial.util; import java.io.ByteArrayOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; /** * Writes the report to the output stream * * @author Krams at {@link http://krams915@blogspot.com} */ public class Writer { private static Logger logger = Logger.getLogger("service"); /** * Writes the report to the output stream */ public static void write(HttpServletResponse response, ByteArrayOutputStream bao) { logger.debug("Writing report to the stream"); try { // Retrieve the output stream ServletOutputStream outputStream = response.getOutputStream(); // Write to the output stream bao.writeTo(outputStream); // Flush the stream outputStream.flush(); // Close the stream outputStream.close(); } catch (Exception e) { logger.error("Unable to write report to the output stream"); } } /** * Writes the report to the output stream */ public static void write(HttpServletResponse response, byte[] byteArray) { logger.debug("Writing report to the stream"); try { // Retrieve the output stream ServletOutputStream outputStream = response.getOutputStream(); // Write to the output stream outputStream.write(byteArray); // Flush the stream outputStream.flush(); // Close the stream outputStream.close(); } catch (Exception e) { logger.error("Unable to write report to the output stream"); } } }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/getphoto?id=1Notice the port number is 8081. That's because I'm using two versions of Tomcat. For the REST provider, I use port 8080. For the REST client, I use port 8081. Depending on how you setup your server, please modify the URL accordingly.
Running the URL displays the following image:
I think you know who this person is (and it just so happen this is the photo I have in my desktop). Notice whatever id you use the application will show the same image. That's because the provider uses a fix image from the classpath. Anyway the point of this exercise is how to retrieve a byte array using RestTemplate.
getAll() method
The getAll() method sends a request to retrieve all records from the REST provider. Once the result has arrived, it is added to the model to be displayed in a JSP page./** * Retrieves all records from the REST provider * and displays the records in a JSP page */ @RequestMapping(value = "/getall", method = RequestMethod.GET) public String getAll(Model model) { logger.debug("Retrieve all persons"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<PersonList> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/persons", HttpMethod.GET, entity, PersonList.class); // Add to model model.addAttribute("persons", result.getBody().getData()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/personspage.jsp return "personspage"; }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/getall
Running the URL shows the following page:
getPerson() method
The getPerson() method retrieves a single record based on the id from the REST provider. Once the result has arrived, it is added to the model to be displayed in a JSP page./** * Retrieves a single record from the REST provider * and displays the result in a JSP page */ @RequestMapping(value = "/get", method = RequestMethod.GET) public String getPerson(@RequestParam("id") Long id, Model model) { logger.debug("Retrieve person with id: " + id); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id); // Add to model model.addAttribute("person", result.getBody()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/getpage.jsp return "getpage"; }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/get?id=1Alternatively we can click the Get link from the getAll page.
Running the URL shows the following page:
getAddPage() and addPerson() methods
The getAddPage() method returns a JSP form where the user can add a new record./** * Retrieves the JSP add page */ @RequestMapping(value = "/add", method = RequestMethod.GET) public String getAddPage(Model model) { logger.debug("Received request to show add page"); // Create new Person and add to model // This is the formBackingOBject model.addAttribute("personAttribute", new Person()); // This will resolve to /WEB-INF/jsp/addpage.jsp return "addpage"; }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/add?id=1Alternatively we can click the Add link from the getAll page
Running the URL shows the following page:
When the form is submitted, the addPerson() method takes control. Its purpose is to send a new record by sending a POST request to the REST provider.
/** * Sends a new record to the REST provider * based on the information submitted from the JSP add page. */ @RequestMapping(value = "/add", method = RequestMethod.POST) public String addPerson(@ModelAttribute("personAttribute") Person person, Model model) { logger.debug("Add new person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers); // Send the request as POST try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person", HttpMethod.POST, entity, Person.class); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; }To see the new record, please refresh the getAll page.
getUpdatePage() and updatePerson() methods
The getUpdatePage() method returns a JSP form where the user can edit an existing record./** * Retrieves the JSP update page */ @RequestMapping(value = "/update", method = RequestMethod.GET) public String getUpdatePage(@RequestParam(value="id", required=true) Integer id, Model model) { logger.debug("Received request to show edit page"); // Retrieve existing Person and add to model // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<Person> entity = new HttpEntity<Person>(headers); // Send the request as GET try { ResponseEntity<Person> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.GET, entity, Person.class, id); // Add to model model.addAttribute("personAttribute", result.getBody()); } catch (Exception e) { logger.error(e); } // This will resolve to /WEB-INF/jsp/updatepage.jsp return "updatepage"; }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/update?id=1Alternatively we can click the Edit link from the getAll page
Running the URL shows the following page:
When the form is submitted, the updatePerson() method takes control. Its purpose is to send an updated record by sending a PUT request to the REST provider.
/** * Sends an update request to the REST provider * based on the information submitted from the JSP update page. */ @RequestMapping(value = "/update", method = RequestMethod.POST) public String updatePerson(@ModelAttribute("personAttribute") Person person, @RequestParam(value="id", required=true) Long id, Model model) { logger.debug("Update existing person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers); // Send the request as PUT ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.PUT, entity, String.class, id); // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; }To see updated record, please refresh the getAll page.
deletePerson() method
The deletePerson() method sends a DELETE request to delete an existing record based on the submitted id./** * Sends a delete request to the REST provider */ @RequestMapping(value = "/delete", method = RequestMethod.GET) public String deletePerson(@RequestParam("id") Long id, Model model) { logger.debug("Delete existing person"); // Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); HttpEntity<String> entity = new HttpEntity<String>(headers); // Send the request as DELETE ResponseEntity<String> result = restTemplate.exchange("http://localhost:8080/spring-rest-provider/krams/person/{id}", HttpMethod.DELETE, entity, String.class, id); // This will resolve to /WEB-INF/jsp/resultpage.jsp return "resultpage"; }
To call this method, we have to use the following URL:
http://localhost:8081/spring-rest-client/krams/delete?id=1Alternatively we can click the Delete link from the getAll page
When you run the URL it will automatically send a request. To see the results, please refresh the getAll page.
Configuration
The application's configuration is quite trivial. It's just a basic Spring MVC configuration. To spare some space, I will not post the XML configurations here. Instead, you can look at them by checking the source code at the end of this tutorial.Considerations
Notice in the header of each request we set the media type to APPLICATION_XML. We can equally change it to APPLICATION_JSON to accept JSON objects.APPLICATION_XML
// Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_XML); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);
APPLICATION_JSON
// Prepare acceptable media type List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>(); acceptableMediaTypes.add(MediaType.APPLICATION_JSON); // Prepare header HttpHeaders headers = new HttpHeaders(); headers.setAccept(acceptableMediaTypes); // Pass the new person and header HttpEntity<Person> entity = new HttpEntity<Person>(person, headers);
For JSON to work, you need to have the Jackson library in your classpath. Make sure to check the pom.xml!
Conclusion
That's it. We've managed to create a REST client using Spring 3's RestTemplate. We've exchanged information with the provider via XML and displayed the result in a JSP page using Spring MVC.Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-rest-guide/
You can download the project as a Maven build. Look for the spring-rest-client.zip in the Download sections.
You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run
If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
Share the joy:
|
Subscribe by reader Subscribe by email Share