@RequestBody và @ResponseBody annotation trong Spring

@RequestBody và @ResponseBody có lẽ là một trong những annotation được sử dụng nhiều nhất trong Spring Rest API. Dùng để ánh xạ dữ liệu truyền từ client lên server và ngược lại.

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu về cách sử dụng 2 annotation này và làm một số ví dụ minh hoạ để hiểu rõ hơn về cách hoạt động của những annotation này.

@RequestBody trong Spring

@RequestBody được dùng để ánh xạ HttpRequest body sang một domain object tự động.

Giả sử chúng ta có Spring controller với login() method nhận vào LoginForm object.

@RestController
public class RequestBodyExampleController {

    @PostMapping("/login")
    public ResponseEntity login (@RequestBody LoginForm form) {

        System.out.println("User name: " + form.getUsername());
        System.out.println("Password: " + form.getPassword());
        return ResponseEntity.ok(HttpStatus.OK);
    }
}

Spring sẽ tự động ánh xạ dữ liệu JSON trong  HttpRequest body sang một Java Type object tương ứng.

Mặc định, tên và kiểu dữ liệu trong JSON phải trùng khớp với tên và kiểu dữ liệu trong Java Type object. 

public class LoginForm {
    private String username;
    private String password;
    // ...
}

Như ví dụ trên, thì client khi request đến /login api thì JSON tương ứng phải là usernamepassword.

curl -i -H "Accept: application/json" 
-H "Content-Type: application/json" 
-X POST --data '{"username": "deft", "password": "123456"}' "http://localhost:8080/login"

---
"OK"

@ResponseBody

@ResponseBody annotation được dùng để thông báo với controller rằng Java Object trả về cho client sẽ tự động ánh xạ sang JSON và chuyển vào HttpResponse.

Giả sử chúng ta có Response object được dùng để trả kết quả về cho client.

public class ResponseTransfer {
    private String text; 
    
    // standard getters/setters
}

Và một controller class

@Controller
@RequestMapping("/login-response")
public class ResponseBodyExampleController {

    @ResponseBody
    @PostMapping
    public ResponseTransfer postResponseController(
            @RequestBody LoginForm loginForm) {
        return new ResponseTransfer("Thanks For Posting!!!");
    }
}

Kết quả khi mình request lên login-response api sẽ như sau:

curl -i 
-H "Accept: application/json" 
-H "Content-Type: application/json" 
-X POST --data '{"username": "deft", "password": "123456"}' 
"http://localhost:8080/login-response"
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 24 Dec 2020 16:16:39 GMT
----
{"text":"Thanks For Posting!!!"}

Các bạn nhớ để ý rằng mình chú thích ResponseBodyExampleController@Controller chứ không phải là @RestController vì bản thân @RestController đã bao gồm @ResponseBody sẽ được áp dụng cho toàn bộ các method bên trong nên không cần phải chú thích @ResponseBody khi sử dụng @RestController đâu nhé.

@ResponseBody với Content-Type

Khi sử dụng @ResponseBody annotation,  chúng ta còn có thể chỉ định content-type trả về cho client như ví dụ trước content-type là JSON, chúng ta có thể thay bằng XML chẳng hạn thông qua thuộc tính produces  trong @RequestMapping, hoặc@PostMapping, @GetMapping là các annotation dạng rút gọn của nó.

 

@PostMapping(value = "/content", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseTransfer postResponseJsonContent(
        @RequestBody LoginForm loginForm) {
    return new ResponseTransfer("JSON Content!");
}

Trong ví dụ, mình đã sử dụng hằng số MediaType.APPLICATION_JSON_VALUE. Ngoài ra, chúng ta có thể sử dụng application / json trực tiếp.

Tiếp theo ,mình triển khai một phương thức với cùng đường dẫn /content api nhưng trả về nội dung XML:

@PostMapping(value = "/content", produces = MediaType.APPLICATION_XML_VALUE)
@ResponseBody
public ResponseTransfer postResponseXmlContent(
        @RequestBody LoginForm loginForm) {
    return new ResponseTransfer("XML Content!");
}

Bây giờ, tuỳ thuộc vào tham số Accept trong header mà bạn truyền lên, mà postResponseJsonContent() haypostResponseXmlContent() sẽ được gọi tương ứng.

Hãy xem Accept là application/json thì kết quả sẽ là

curl -i 
-H "Accept: application/json" 
-H "Content-Type: application/json" 
-X POST --data '{"username": "deft", "password": "123456"}' 
"http://localhost:8080/login-response/content" 
HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Thu, 24 Dec 2020 16:31:30 GMT
-----
{"text":"JSON Content!"}

Còn với XML 

curl -i -H 
"Accept: application/xml" 
-H "Content-Type: application/json"
-X POST --data '{"username": "deft", "password": "123456"}'
 "http://localhost:8080/login-response/content"
HTTP/1.1 200 
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Thu, 24 Dec 2020 16:44:56 GMT

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<responseTransfer><text>XML Content!</text></responseTransfer>

Một số lưu ý khi các bạn sử dụng định dạng XML thì thêm dependency hỗ trợ chuyển đổi XML

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Và thêm @XmlRootElement annotation vào ResponseTransfer class.

@XmlRootElement
public class ResponseTransfer {...}

Kết bài

Như vậy là chúng ta đã tìm hiểu được 2 annotation thường xuyên được sử dụng trong Spring là @RequestBody@ResponseBody. Lưu ý ở trên mình dùng terminal để request server. Các bạn có thể sử dụng các tool khác như Postman cũng được cho dễ sử dụng.

Mã nguồn được mình công khai trên gitlab để các bạn có thể tiện theo dõi và kiểm thử: source-code

Nguồn tham khảo

Spring Boot Rest XML example – Web service with XML Response

https://stackoverflow.com/questions/63832966/httpmessagenotwritableexception-no-converter-for-with-preset-content-type

https://www.baeldung.com/spring-request-response-body

4.7 7 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x