Trả về kết quả
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 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
📬 ResponseEntityT 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 headerLocationchỉ 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ứchttp://localhost:5000/api/products.path("/{id}")nối thêm phần{id}, rồibuildAndExpandđ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 404POST 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ả
nullkhi 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âuMethod xoá sản phẩm thành công và không cần trả dữ liệu gì. Nên trả gì?
Method trả ResponseEntity<Order>. Không tìm thấy đơn hàng thì viết gì?
Sau khi tạo khách hàng mới, response có header Location. Header này dùng để làm gì?