REST API 实践
实验目标
构建完整的 RESTful API,掌握 Spring MVC 的核心功能。
实验 1:完整 CRUD API
java
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductService productService;
@GetMapping
public ResponseEntity<List<Product>> getAllProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
Page<Product> products = productService.findAll(PageRequest.of(page, size));
return ResponseEntity.ok(products.getContent());
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
return productService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
Product saved = productService.save(product);
return ResponseEntity
.created(URI.create("/api/products/" + saved.getId()))
.body(saved);
}
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(
@PathVariable Long id,
@Valid @RequestBody Product product
) {
if (!productService.exists(id)) {
return ResponseEntity.notFound().build();
}
product.setId(id);
return ResponseEntity.ok(productService.save(product));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
if (!productService.exists(id)) {
return ResponseEntity.notFound().build();
}
productService.delete(id);
return ResponseEntity.noContent().build();
}
}实验 2:数据验证
java
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Product name is required")
@Size(min = 3, max = 100)
private String name;
@NotNull
@DecimalMin(value = "0.01", message = "Price must be greater than 0")
private BigDecimal price;
@Min(0)
private Integer stock;
// getters and setters
}
@ControllerAdvice
public class ValidationExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidation(
MethodArgumentNotValidException ex
) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
}测试:
bash
# 创建产品(成功)
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{"name":"Laptop","price":999.99,"stock":10}'
# 创建产品(验证失败)
curl -X POST http://localhost:8080/api/products \
-H "Content-Type: application/json" \
-d '{"name":"","price":-10,"stock":-5}'实验 3:查询和过滤
java
@GetMapping("/search")
public List<Product> searchProducts(
@RequestParam(required = false) String name,
@RequestParam(required = false) BigDecimal minPrice,
@RequestParam(required = false) BigDecimal maxPrice
) {
return productService.search(name, minPrice, maxPrice);
}
// Service 层
public List<Product> search(String name, BigDecimal minPrice, BigDecimal maxPrice) {
Specification<Product> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null && !name.isEmpty()) {
predicates.add(cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%"));
}
if (minPrice != null) {
predicates.add(cb.greaterThanOrEqualTo(root.get("price"), minPrice));
}
if (maxPrice != null) {
predicates.add(cb.lessThanOrEqualTo(root.get("price"), maxPrice));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
return productRepository.findAll(spec);
}实验 4:内容协商
java
@GetMapping(value = "/{id}", produces = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
public Product getProduct(@PathVariable Long id) {
return productService.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product not found"));
}测试:
bash
# JSON 响应
curl -H "Accept: application/json" http://localhost:8080/api/products/1
# XML 响应
curl -H "Accept: application/xml" http://localhost:8080/api/products/1实验 5:分页和排序
java
@GetMapping
public Page<Product> getProducts(
@PageableDefault(size = 10, sort = "name", direction = Sort.Direction.ASC)
Pageable pageable
) {
return productService.findAll(pageable);
}测试:
bash
# 第一页,每页 10 条
curl "http://localhost:8080/api/products?page=0&size=10"
# 按价格降序排序
curl "http://localhost:8080/api/products?sort=price,desc"
# 多字段排序
curl "http://localhost:8080/api/products?sort=price,desc&sort=name,asc"验收标准
✅ CRUD 操作正常
✅ 数据验证正确
✅ 查询和过滤功能完整
✅ 分页和排序正常
✅ 返回正确的 HTTP 状态码
返回第二章概览