VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Spring Boot Web APIXây API CRUD
Bài 5/19
5 phút

Trả về kết quả

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Ở bài Controller và routing, gọi API lấy sản phẩm không tồn tại thì nhận về 200 với body rỗng thay vì 404. Client không phân biệt được "không có" với "thành công". Bài này chỉ cách chọn đúng status code cho từng trường hợp.

Khái niệm

📬 ResponseEntity: kiểu trả về của handler method, gói dữ liệu kiểu T cùng status code và header của response.

Các method có sẵn của ResponseEntity để tạo kết quả:

Method Status code Khi nào dùng
ok(data) 200 đọc thành công, có dữ liệu
created(uri).body(data) 201 tạo mới thành công
noContent().build() 204 thành công, không có gì trả về
badRequest().build() 400 dữ liệu gửi lên sai
notFound().build() 404 không tìm thấy

Ví dụ

// ProductsController.java
package com.shop.api;

import java.net.URI;
import java.util.*;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.*;

@RestController
@RequestMapping("/api/products")
class ProductsController {
    private final List<Product> products =
            new ArrayList<>();

    @GetMapping("/{id}")
    public ResponseEntity<Product> getById(
            @PathVariable int id) {
        var product = products.stream()
                .filter(p -> p.getId() == id)
                .findFirst()
                .orElse(null);
        if (product == null) {
            return ResponseEntity.notFound().build();
        }
        return ResponseEntity.ok(product);
    }

    @PostMapping
    public ResponseEntity<Product> create(
            @RequestBody Product product) {
        product.setId(products.size() + 1);
        products.add(product);
        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(product.getId())
                .toUri();
        return ResponseEntity.created(location)
                .body(product);
    }
}

// Product.java
package com.shop.api;

class Product {
    private int id;
    private String name = "";
    private long price;

    public int getId() { return id; }
    public String getName() { return name; }
    public long getPrice() { return price; }

    public void setId(int id) { this.id = id; }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(long price) {
        this.price = price;
    }
}
  • ResponseEntity<Product> cho phép trả notFound() ở nhánh này và ok(product) ở nhánh kia.
  • created(location) trả 201, kèm header Location chỉ tới URL của sản phẩm vừa tạo, ví dụ /api/products/1.
  • fromCurrentRequest() lấy URL của request đang xử lý, tức http://localhost:5000/api/products.
  • path("/{id}") nối thêm phần {id}, rồi buildAndExpand điền id của sản phẩm vào chỗ đó.

Thử ngay

Giữ các file cũ, thay ProductsController.java và Product.java theo phần Ví dụ, rồi chạy lại server. Tạo file product.json có nội dung { "name": "Pen", "price": 5000 }, rồi gọi:

curl -i -X POST http://localhost:5000/api/products -H "Content-Type: application/json" -d @product.json
curl -i http://localhost:5000/api/products/1
curl -i http://localhost:5000/api/products/99

Đoán trước khi chạy: lần gọi POST trả về status code nào, và lần gọi cuối còn là 200 với body rỗng không?

Xem kết quả
HTTP/1.1 201
Location: http://localhost:5000/api/products/1
{"id":1,"name":"Pen","price":5000}

HTTP/1.1 200
{"id":1,"name":"Pen","price":5000}

HTTP/1.1 404

POST trả 201 kèm Location. Sản phẩm 99 giờ trả 404, vì method chủ động trả notFound() thay vì trả null.

Lỗi hay gặp

Trả null khi không tìm thấy. Client nhận 200 với body rỗng, tưởng là thành công.

// SAI — không tìm thấy mà vẫn là "thành công"
// OrdersController.java
package com.shop.api;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/orders")
class OrdersController {
    @GetMapping("/{id}")
    public String getById(@PathVariable int id) {
        return null;
    }
}
// ĐÚNG
// OrdersController.java
package com.shop.api;

import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/orders")
class OrdersController {
    @GetMapping("/{id}")
    public ResponseEntity<String> getById(
            @PathVariable int id) {
        return ResponseEntity.notFound().build();
    }
}

Tạo mới mà trả 200. Tạo thành công nên trả 201 qua ResponseEntity.created, để client biết ngay URL của thứ vừa tạo.

Tóm tắt

  • Dùng ResponseEntity<T> để một method trả được cả dữ liệu lẫn lỗi.
  • Đọc được thì ok, tạo mới thì created, không có thì notFound.
  • Không trả null khi không tìm thấy, vì client sẽ nhận 200 với body rỗng.
  • Status code đúng giúp client xử lý mà không phải đọc body.

Tự kiểm tra

0/3 câu
Câu 1

Method xoá sản phẩm thành công và không cần trả dữ liệu gì. Nên trả gì?

Câu 2

Method trả ResponseEntity<Order>. Không tìm thấy đơn hàng thì viết gì?

Câu 3

Sau khi tạo khách hàng mới, response có header Location. Header này dùng để làm gì?

Nhận dữ liệu từ requestDTO

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt