定义 restful 风格 url 的核心在于使用名词复数表示资源集合,并通过 http 动词区分操作类型,同时利用路径参数或查询参数处理过滤与分页。具体做法是将控制器映射到如 /api/users 这样的资源端点,在方法上使用 @getmapping、@postmapping 等注解来明确语义,确保 url 结构清晰且符合标准规范。

如何设计用户资源的 CRUD 端点
针对用户管理场景,RESTful 设计通常将 /users 作为根资源,通过不同的 HTTP 方法实现增删改查。创建用户时使用 POST 请求,获取列表时结合查询参数进行过滤,更新单个用户则指向 /users/{id} 路径。

package com.example.demo;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final Map<Long, User> userStore = new ConcurrentHashMap<>();
private long idCounter = 1;
@GetMapping
public List<User> getUsers(@RequestParam(required = false) String name) {
if (name == null) return new ArrayList<>(userStore.values());
return userStore.values().stream()
.filter(u -> u.getName().contains(name))
.toList();
}
@PostMapping
public User createUser(@RequestBody User user) {
user.setId(idCounter++);
userStore.put(user.getId(), user);
return user;
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userStore.get(id);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
user.setId(id);
userStore.put(id, user);
return user;
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userStore.remove(id);
}
}
class User {
private Long id;
private String name;
// Getters and Setters omitted for brevity
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
预期结果:POST /api/users 返回包含新 ID 的用户对象;GET /api/users?name=John 返回匹配的用户列表;GET /api/users/1 返回特定 ID 的用户详情。
如何处理嵌套资源与关联数据
当需要访问属于某个父资源的子资源时,应在 URL 中体现层级关系,例如 /users/{userId}/posts。这种设计保持了资源的独立性,同时清晰地表达了所有权和归属关系,避免在 URL 中使用动词如 getPostsByUserId。

package com.example.demo;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/api/users")
public class NestedResourceController {
private final Map<Long, User> users = new ConcurrentHashMap<>();
private final Map<Long, Post> posts = new ConcurrentHashMap<>();
private long postIdCounter = 1;
@GetMapping("/{userId}/posts")
public List<Post> getUserPosts(@PathVariable Long userId) {
if (!users.containsKey(userId)) return new ArrayList<>();
return posts.values().stream()
.filter(p -> p.getUserId().equals(userId))
.toList();
}
@PostMapping("/{userId}/posts")
public Post createPostForUser(@PathVariable Long userId, @RequestBody Post post) {
if (!users.containsKey(userId)) return null;
post.setId(postIdCounter++);
post.setUserId(userId);
posts.put(post.getId(), post);
return post;
}
}
class Post {
private Long id;
private Long userId;
private String content;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Long getUserId() { return userId; }
public void setUserId(Long userId) { this.userId = userId; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
}
预期结果:POST /api/users/1/posts 创建归属于用户 1 的帖子并返回其 ID;GET /api/users/1/posts 仅返回该用户的帖子列表。
基于路径参数 ID 的查找行为
在 @GetMapping("/{id}") 的定义中,Spring Boot 会将 URL 中的数字部分绑定到 @PathVariable 参数。如果传入的 ID 在存储中不存在,默认行为是返回 404 Not Found 状态码,除非代码中显式处理了空值情况并返回自定义响应。


















