Saturday, 17 May 2014

Spring HATEOAS



We can download a sample project in the below mentioned URL.
https://github.com/krishnakumarsamy/Spring.git

This project is used to give basic idea of

1. How to write Spring REST web service.
2. How to create custom message converters in spring
3. How to use spring HATEOAS in REST.
4. JSON/XML conversion of custom objects.
5. JSON/XML conversion of List
6. How to avoid default values while serialization in (XML/JSON).
7. How to use @ControllerAdvice to handle all the exceptions from all the controllers.
8. How to use @ExceptionHandler to handle exception in controller.
9. How to handle method not supported exception using (@ExceptionHandler(HttpRequestMethodNotSupportedException.class)).
10. How to handle custom defined exception from all the controller using (@ControllerAdvice)
11. How to use @PathVariable in spring REST


Download the sample from the above location.
Steps to execute: 
1. Go inside hypermedia folder.
2. Issue the command mvn package
3. the war will be created in hypermedia/target folder. Copy it into tomcat.../.../webapps/ folder and start tomcat.

@RequestMapping(value = "/all", produces = { "text/plain", "text/html",
  "application/xml", "application/json" })
public ResponseEntity<StudentDAO> getStudentDetails() {
 return new ResponseEntity<StudentDAO>(studentdao, HttpStatus.OK);
}

In the above code conversion of StudentDAO will be done automatically by adding jackson jars into class path.

How to create custom message converters in spring:

There is a custom converter has been added to convert the StudentDAO object into custom formats like (text/html and text/plain).

Below is the code

public class StudentMessageConverter extends AbstractHttpMessageConverter<StudentDAO> {
 public StudentMessageConverter() {
  super(new MediaType("text", "plain", Charset.forName("UTF-8")), new MediaType("text",
    "html", Charset.forName("UTF-8")));
 }

 @Override
 protected boolean supports(Class<?> clazz) {
  return clazz.getSimpleName().equalsIgnoreCase("studentdao");
 }

 @Override
 protected StudentDAO readInternal(Class<? extends StudentDAO> clazz,
   HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
  return null;
 }

 @Override
 protected void writeInternal(StudentDAO studentDao, HttpOutputMessage outputMessage)
   throws IOException, HttpMessageNotWritableException {
  StringBuffer studentRecords = new StringBuffer();
  int count = 1;
  for (Student student : studentDao.getStudentList()) {
   studentRecords.append("\n\nRecord").append(count);
   studentRecords.append("\nId").append(" = ").append(student.getStudentId())
     .append("\nName").append(" = ").append(student.getStudentName()).append("\n")
     .append("Age").append(" = ").append(student.getAge());
   count++;
  }
  outputMessage.getBody().write(studentRecords.toString().getBytes());

 }

}
For complete code refer the sample(StudentMessageConverter.java).


How to use spring HATEOAS in REST:

@RequestMapping(value = "/")
public ResponseEntity<Student> getLinks() throws HypermediaException {
 student = new Student();
 for (Student student1 : studentdao.getStudentList()) {
  student.add(linkTo(
    methodOn(StudentController.class).getStudent(student1.getStudentId())).withRel(
    "Get_Student" + student1.getStudentId()));
 }
 student.add(linkTo(methodOn(StudentController.class).getStudentDetails()).withRel(
   "Get_All_Students"));
 return new ResponseEntity<Student>(student, HttpStatus.OK);
}
We have to extend ResourceSupport class of spring HATEOAS and this will give flexibility of adding links in the response.

Refer - Student.java


How to avoid default values while serialization in (XML/JSON):

The  below annotation will help to avoid default values while serialization.

@JsonInclude(Include.NON_EMPTY)

Refer - Student.java


How to use @ControllerAdvice to handle all the exceptions from all the controllers:
@ControllerAdvice annotation is used to handle global exceptions. If any of the exception we need to handle commonly for all the controllers then we can use this annotation.

Sample :
@ControllerAdvice
public class CommonControllerExceptionHandler {...}
Refer - CommonControllerExceptionHandler.java

Inside the class use @ExceptionHandler annotation to handle individual exceptions.

Ex : @ExceptionHandler(HypermediaException.class)
In the above annotation  HypermediaException is a custom exception class which extends Exception class.

@PathVariable:

This annotation is used to map values which comes from request URL.

@RequestMapping(value = "/get/{id}", produces = { "application/xml",
  "application/json" }, method = { RequestMethod.GET }) 
If we need to get the student 1 details then we can form URL as

/student/1

For more details refer - StudentController.java


How to verify the result:
http://localhost:8080/hypermedia/
The above URL is the base URL. If we hit the base URL then we will get list of URLS in the response.

Client need not depend on server side changes. If we decided the rel value then in server side we can make changes independently. If the URL is hard coded then for every server side changes we need to implement the same in client also. So HATEOAS will avoid the dependency.

We can use the REST client which is the add-on for firefox.
or in Unix / MAC/ Linux use curl command

Request:
http://localhost:8080/hypermedia/
Response:
<student>
<atom:link rel="Get_Student1" href= "http://localhost:8080/hypermedia/student/1" />
<atom:link rel="Get_Student2" href= "http://localhost:8080/hypermedia/student/2" />
<atom:link rel="Get_Student3" href= "http://localhost:8080/hypermedia/student/3" />
<atom:link rel="Get_Student4" href= "http://localhost:8080/hypermedia/student/4" />
<atom:link rel="Get_All_Students" href= "http://localhost:8080/hypermedia/student/all" />
</student>
we can use any one of the above URL to get the details.

Ex Get_Student1 Link:

Request :
http://localhost:8080/hypermedia/student/1

Response XML: 

<student>
<age>21</age>
<regNo>2</regNo>
<name>Ram</name>
</student>

Request using curl command:
 curl -H Accept:application/json http://localhost:8080/hypermedia/student/1 
Response JSON: 
{"regNo":"2","name":"Ram","age":"21"}