Java案例如何实现REST API?

wen python案例 1

本文目录导读:

Java案例如何实现REST API?

  1. 基于Spring Boot的实现(最常用)
  2. API测试
  3. REST API设计建议
  4. 其他实现方式

我来详细讲解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的完整流程,包括实体的定义、控制器的实现、异常处理以及测试,你可以根据实际需求选择合适的技术栈。

抱歉,评论功能暂时关闭!