本文目录导读:

我来详细讲解Java实现REST API的几种主流方式及完整案例。
基于Spring Boot的实现(最常用)
项目结构
rest-api-demo/
├── src/main/java/com/example/demo/
│ ├── controller/
│ │ └── UserController.java
│ ├── model/
│ │ └── User.java
│ ├── service/
│ │ └── UserService.java
│ └── DemoApplication.java
├── src/main/resources/
│ └── application.yml
└── pom.xml
pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>rest-api-demo</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
实体类(Model)
package com.example.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.*;
import java.time.LocalDateTime;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度2-20个字符")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 0, message = "年龄不能小于0")
@Max(value = 150, message = "年龄不能超过150")
private Integer age;
private LocalDateTime createTime;
}
控制器(Controller)
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
// GET - 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
return ResponseEntity.ok(userService.findAll());
}
// GET - 根据ID获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// POST - 创建用户
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User createUser(@Valid @RequestBody User user) {
return userService.create(user);
}
// PUT - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@Valid @RequestBody User user) {
return userService.update(id, user)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
// DELETE - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (userService.delete(id)) {
return ResponseEntity.noContent().build();
}
return ResponseEntity.notFound().build();
}
// GET - 根据条件查询
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(
@RequestParam(required = false) String name,
@RequestParam(required = false) Integer age) {
return ResponseEntity.ok(userService.search(name, age));
}
}
服务层(Service)
package com.example.demo.service;
import com.example.demo.model.User;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@Service
public class UserService {
private final Map<Long, User> users = new ConcurrentHashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1);
public List<User> findAll() {
return new ArrayList<>(users.values());
}
public Optional<User> findById(Long id) {
return Optional.ofNullable(users.get(id));
}
public User create(User user) {
Long id = idGenerator.getAndIncrement();
user.setId(id);
user.setCreateTime(LocalDateTime.now());
users.put(id, user);
return user;
}
public Optional<User> update(Long id, User user) {
if (users.containsKey(id)) {
user.setId(id);
user.setCreateTime(users.get(id).getCreateTime());
users.put(id, user);
return Optional.of(user);
}
return Optional.empty();
}
public boolean delete(Long id) {
return users.remove(id) != null;
}
public List<User> search(String name, Integer age) {
return users.values().stream()
.filter(u -> name == null || u.getUsername().contains(name))
.filter(u -> age == null || u.getAge().equals(age))
.collect(Collectors.toList());
}
}
application.yml配置
server:
port: 8080
spring:
application:
name: rest-api-demo
全局异常处理
package com.example.demo.config;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return ResponseEntity.badRequest().body(errors);
}
}
API测试
使用curl测试
# 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"username":"张三","email":"zhangsan@example.com","age":25}'
# 获取所有用户
curl http://localhost:8080/api/users
# 获取单个用户
curl http://localhost:8080/api/users/1
# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{"username":"李四","email":"lisi@example.com","age":30}'
# 删除用户
curl -X DELETE http://localhost:8080/api/users/1
# 搜索用户
curl "http://localhost:8080/api/users/search?name=张&age=25"
使用JUnit测试
package com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
public void testCreateUser() throws Exception {
User user = new User();
user.setUsername("测试用户");
user.setEmail("test@example.com");
user.setAge(25);
when(userService.create(any(User.class))).thenReturn(user);
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(user)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.username").value("测试用户"));
}
}
REST API设计建议
URL设计规范
// 集合资源
GET /api/users // 获取用户列表
POST /api/users // 创建用户
GET /api/users/{id} // 获取单个用户
PUT /api/users/{id} // 更新用户
DELETE /api/users/{id} // 删除用户
// 子资源
GET /api/users/{id}/orders // 获取用户的订单
POST /api/users/{id}/orders // 创建用户订单
// 过滤和排序
GET /api/users?page=0&size=10&sort=age,desc
GET /api/users?name=张三&age=25
响应格式
// 成功响应
{
"status": 200,
"message": "success",
"data": {
"id": 1,
"username": "张三",
"email": "zhangsan@example.com",
"age": 25
}
}
// 错误响应
{
"status": 400,
"message": "验证失败",
"errors": {
"username": "用户名不能为空",
"email": "邮箱格式不正确"
}
}
// 分页响应
{
"status": 200,
"data": {
"content": [...],
"totalElements": 100,
"totalPages": 10,
"number": 0,
"size": 10
}
}
其他实现方式
使用JAX-RS
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
@Path("/api/users")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class UserResource {
@GET
public Response getAllUsers() {
return Response.ok(userService.findAll()).build();
}
@GET
@Path("/{id}")
public Response getUserById(@PathParam("id") Long id) {
return userService.findById(id)
.map(u -> Response.ok(u).build())
.orElse(Response.status(404).build());
}
@POST
public Response createUser(User user) {
return Response.status(Response.Status.CREATED)
.entity(userService.create(user))
.build();
}
}
使用Micronaut
import io.micronaut.http.annotation.*;
import io.micronaut.http.HttpStatus;
import javax.validation.Valid;
@Controller("/api/users")
public class UserController {
@Get
public List<User> getAll() {
return userService.findAll();
}
@Post
@Status(HttpStatus.CREATED)
public User create(@Body @Valid User user) {
return userService.create(user);
}
}
这个案例涵盖了创建REST API的完整流程,包括实体的定义、控制器的实现、异常处理以及测试,你可以根据实际需求选择合适的技术栈。