Spring REST
Introduction
What is REST?
Compared to traditional MVC framework, an application will be separated into different parts so as to facilitate the maintenance and different parts of application can also be coded by different framework, such as nodeJS Express, java spring
In order to build the communication between each parts, the api url will be called by using Http
Each api call should be stateless
The common data format will be Json, Xml
Request
Http Method (Get, Post, Put, Delete)
Header (For additional information)
Body (passed in Json format)
Response
Status Code and Protocol
Header
Body(the return value)
Terminology
Data Binding separate into 2 parts
Serialization
Convert Object into Stream (JSON is one of the form of the stream to be transfer through http call)
Deserialization
Convert Stream into Object
Data Binding
Pre-Action
Install the dependency of FastXML Jackson
<!-- Add Jackson for JSON converters -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
It is a dependency that bind the json into java POJO, otherwise, application/json format will not be accepted by api end point
Situation 1
Data
{
"name": "Tom",
"age" : 18
}
Modal
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Student {
private String name;
private Integer age;
...
}
Situation 2
Json may contain another another json which have not a constant format
Json only contain value in string or number format, if we want convert the string into date in POJO, we need to do custom serialization and deserialization
Data
{
theName:"Tom",
createDate: "2020/03/02",
rawData:{
...
}
}
Modal
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Record{
@JsonProperty("theName")
private String name;
private Object rawData;
@JsonSerialize(using = CustomDateSerializer.class)
@JsonDeserialize(using = CustomDateDeserializer.class)
private LocalDate createDate;
...
}
Custom Serializer and Deserializer
public class CustomDateSerializer extends JsonSerializer<LocalDate> {
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
// replace the method of serializer by converting localdate back to string
@Override
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
try{
jsonGenerator.writeString(localDate.format(formatter));
}
catch(Exception e){
throw new IOException("Wrong Date Format");
}
}
}
public class CustomDateDeserializer extends JsonDeserializer<LocalDate> {
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
// replace the method of default deserializer by turning the string into localdate format
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
String dateStr = jsonParser.getText();
try{}
catch (Exception e){
throw new IOException("Wrong Format");
}
return LocalDate.parse(dateStr, formatter);
}
}
Controller
@PostMapping(value = "/record")
public Student postStudent(@RequestBody Record student){
return record;
}
// output
// {
// "name" : "Tom",
// "createDate": "02-03-2020",
// "rawData": {
// ...
// }
// }
Situation 3
Sometimes, we may need to json in string format
we need to make good use of object mapper from Jackson to handle that
Modal
public class Student
{
private String name;
private HashMap<String,String> address;
private List<String> hobbies;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HashMap<String, String> getAddress() {
return address;
}
public void setAddress(HashMap<String, String> address) {
this.address = address;
}
public List<String> getHobbies() {
return hobbies;
}
public void setHobbies(List<String> hobbies) {
this.hobbies = hobbies;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", address=" + address +
", hobbies=" + hobbies +
'}';
}
}
Main
public static void main( String[] args ) {
try {
ObjectMapper objectMapper = new ObjectMapper();
String json = "" +
"{" +
"\"name\" : \"Sam\", " +
"\"address\" : {\"room\": \"107\"}," +
"\"hobbies\" : [\"Football\", \"Basketball\"],"+
"\"gg\": \"test\""+
"}";
Student student = objectMapper.readValue(json, Student.class);
System.out.println(student.getName());
System.out.println(student.getAddress().get("room"));
List<String> hobbyList = student.getHobbies();
hobbyList.stream().forEach(hobby->{
System.out.println(hobby);
});
String studentJson = objectMapper.writeValueAsString(student);
System.out.println(student);
System.out.println(studentJson);
}
catch (Exception e){
System.out.println("msg: " +e.getMessage());
}
}
// Output:
// Sam
// 107
// Football
// Basketball
// Student{name='Sam', address={room=107}, hobbies=[Football, Basketball]}
// {"name":"Sam","address":{"room":"107"},"hobbies":["Football","Basketball"]}
Exception Handling
In some of the case, such as: the object cannot be found by using id entered by user
The error json should be returned in order to notice the user
Custom Exception
public class StudentNotFoundException extends RuntimeException{
public StudentNotFoundException(String message) {
super(message);
}
}
Error POJO
public class ErrorResponse {
private Integer status;
private String message;
private Long timeStamp;
...
}
Exception Handler
// This annotation is made from aop , the advice type may be around
// in order to monitor the method of the controller
@ControllerAdvice
public class RestExceptionHandler {
// handle 2 custom types of exception
@ExceptionHandler({StudentNotFoundException.class, DateNotMatchException.class})
public ResponseEntity<ErrorResponse> handleException(Exception exception){
ErrorResponse error = new ErrorResponse();
error.setStatus(HttpStatus.NOT_FOUND.value());
error.setMessage(exception.getMessage());
error.setTimeStamp(System.currentTimeMillis());
return new ResponseEntity<>(error ,HttpStatus.NOT_FOUND);
}
}
Controller
@GetMapping("/student/{studentid}")
public ResponseEntity<Student> getStudent(@PathVariable Integer studentid, HttpServletRequest request){
if(studentid >= studentList.size() || studentid < 0){
throw new StudentNotFoundException("Student Not Found");
}
return new ResponseEntity<>(studentList.get(studentid), HttpStatus.ACCEPTED);
}
Result
Last updated
Was this helpful?