AI摘要

文章系统梳理RESTful API设计全流程:资源导向URL、状态码、版本策略;统一请求响应与分页格式;分组参数校验;Swagger/Spring REST Docs文档化;JWT安全、限流及性能监控切面实践,为前后端协作提供可落地规范。

(一) RESTful API设计深度实践

1. 资源导向的URL设计

@RestController
@RequestMapping("/api/v1")
public class UserController {
    
    // 好的设计:资源导向,HTTP方法明确
    @GetMapping("/users")              // 获取用户列表
    @PostMapping("/users")             // 创建用户
    @GetMapping("/users/{id}")         // 获取特定用户
    @PutMapping("/users/{id}")         // 全量更新用户
    @PatchMapping("/users/{id}")        // 部分更新用户
    @DeleteMapping("/users/{id}")      // 删除用户
    
    // 针对资源的操作
    @PostMapping("/users/{id}/activate")   // 激活用户
    @GetMapping("/users/{id}/orders")       // 获取用户的订单
}

2. HTTP状态码的正确使用

@RestController
public class ResponseCodeController {
    
    @PostMapping("/users")
    public ResponseEntity<UserVO> createUser(@Valid @RequestBody UserDTO userDTO) {
        try {
            UserVO user = userService.createUser(userDTO);
            return ResponseEntity.status(HttpStatus.CREATED).body(user);
        } catch (DuplicateUserException e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }
    }
    
    @GetMapping("/users/{id}")
    public ResponseEntity<UserVO> getUser(@PathVariable Long id) {
        return userService.findById(id)
                .map(user -> ResponseEntity.ok(user))
                .orElse(ResponseEntity.notFound().build());
    }
    
    @PutMapping("/users/{id}")
    public ResponseEntity<UserVO> updateUser(@PathVariable Long id, 
                                           @Valid @RequestBody UserDTO userDTO) {
        try {
            UserVO user = userService.updateUser(id, userDTO);
            return ResponseEntity.ok(user);
        } catch (UserNotFoundException e) {
            return ResponseEntity.notFound().build();
        }
    }
}

3. 版本管理策略对比

// 方案1:URI路径版本(最常用)
@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 { /* ... */ }

@RestController  
@RequestMapping("/api/v2/users")
public class UserControllerV2 { /* ... */ }

// 方案2:请求头版本
@GetMapping(value = "/users", headers = "API-Version=1")
public ResponseEntity<?> getUsersV1() { /* ... */ }

@GetMapping(value = "/users", headers = "API-Version=2")  
public ResponseEntity<?> getUsersV2() { /* ... */ }

// 方案3:媒体类型版本
@GetMapping(value = "/users", produces = "application/vnd.company.v1+json")
public ResponseEntity<?> getUsersV1() { /* ... */ }

(二) 请求和响应设计规范

1. 统一的请求/响应格式

@Data
public class ApiRequest<T> {
    private String requestId;      // 请求ID,用于链路追踪
    private Long timestamp;       // 请求时间戳
    private T data;               // 请求数据
    private Map<String, String> ext; // 扩展字段
}

@Data 
public class ApiResponse<T> {
    private Boolean success;      // 是否成功
    private Integer code;         // 状态码
    private String message;       // 消息
    private T data;               // 响应数据
    private String traceId;       // 链路追踪ID
    private Long timestamp;       // 响应时间戳
    
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(true);
        response.setCode(200);
        response.setMessage("成功");
        response.setData(data);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
    
    public static ApiResponse<?> error(Integer code, String message) {
        ApiResponse<Object> response = new ApiResponse<>();
        response.setSuccess(false);
        response.setCode(code);
        response.setMessage(message);
        response.setTimestamp(System.currentTimeMillis());
        return response;
    }
}

2. 分页响应标准化

@Data
public class PageResponse<T> {
    private List<T> list;          // 当前页数据
    private Long total;            // 总记录数
    private Integer pageSize;      // 每页大小
    private Integer pageNum;       // 当前页码
    private Integer totalPages;    // 总页数
    private Boolean hasNext;       // 是否有下一页
    private Boolean hasPrevious;   // 是否有上一页
    
    public static <T> PageResponse<T> of(List<T> list, Long total, Integer pageNum, Integer pageSize) {
        PageResponse<T> response = new PageResponse<>();
        response.setList(list);
        response.setTotal(total);
        response.setPageNum(pageNum);
        response.setPageSize(pageSize);
        response.setTotalPages((int) Math.ceil((double) total / pageSize));
        response.setHasNext(pageNum < response.getTotalPages());
        response.setHasPrevious(pageNum > 1);
        return response;
    }
}

// 在Controller中的使用
@GetMapping("/users")
public ApiResponse<PageResponse<UserVO>> getUsers(
        @RequestParam(defaultValue = "1") Integer pageNum,
        @RequestParam(defaultValue = "20") Integer pageSize,
        UserQuery query) {
    
    PageHelper.startPage(pageNum, pageSize);
    List<UserVO> users = userService.findUsers(query);
    PageInfo<UserVO> pageInfo = new PageInfo<>(users);
    
    PageResponse<UserVO> pageResponse = PageResponse.of(
        users, pageInfo.getTotal(), pageNum, pageSize
    );
    
    return ApiResponse.success(pageResponse);
}

(三) 参数校验和错误处理

1. 验证注解的深度使用

@Data
public class UserDTO {
    @NotBlank(message = "用户名不能为空")
    @Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
    @Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "用户名只能包含字母、数字和下划线")
    private String username;
    
    @Email(message = "邮箱格式不正确")
    private String email;
    
    @NotNull(message = "年龄不能为空")
    @Min(value = 1, message = "年龄必须大于0")
    @Max(value = 150, message = "年龄必须小于150")
    private Integer age;
    
    @NotNull(message = "创建时间不能为空")
    @Past(message = "创建时间必须是过去时间")
    private LocalDateTime createTime;
    
    @Valid // 嵌套验证
    private AddressDTO address;
    
    @AssertTrue(message = "密码和确认密码必须一致")
    public boolean isPasswordMatch() {
        return password != null && password.equals(confirmPassword);
    }
}

@Data
public class AddressDTO {
    @NotBlank(message = "省份不能为空")
    private String province;
    
    @NotBlank(message = "城市不能为空") 
    private String city;
}

2. 分组验证

// 定义验证组
public interface CreateGroup {}
public interface UpdateGroup {}

@Data
public class UserDTO {
    @Null(groups = CreateGroup.class, message = "创建时ID必须为空")
    @NotNull(groups = UpdateGroup.class, message = "更新时ID不能为空")
    private Long id;
    
    @NotBlank(groups = {CreateGroup.class, UpdateGroup.class})
    private String username;
}

// 在Controller中使用分组验证
@PostMapping("/users")
public ApiResponse<?> createUser(@Validated(CreateGroup.class) @RequestBody UserDTO userDTO) {
    // 创建用户逻辑
}

@PutMapping("/users/{id}")  
public ApiResponse<?> updateUser(@PathVariable Long id,
                               @Validated(UpdateGroup.class) @RequestBody UserDTO userDTO) {
    // 更新用户逻辑
}

(四) API文档化和测试

1. 使用Swagger/OpenAPI生成文档

@Configuration
@EnableOpenApi
public class SwaggerConfig {
    
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
                .paths(PathSelectors.any())
                .build()
                .globalRequestParameters(globalRequestParameters())
                .globalResponses(HttpMethod.GET, globalResponseMessages());
    }
    
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("用户服务API文档")
                .description("用户管理相关接口")
                .version("1.0")
                .contact(new Contact("开发团队", "", "dev@example.com"))
                .build();
    }
}

// 在Controller中使用注解
@RestController
@Api(tags = "用户管理")
@RequestMapping("/api/v1/users")
public class UserController {
    
    @ApiOperation("创建用户")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userDTO", value = "用户信息", required = true)
    })
    @ApiResponses({
        @ApiResponse(code = 201, message = "创建成功"),
        @ApiResponse(code = 400, message = "参数错误")
    })
    @PostMapping
    public ResponseEntity<UserVO> createUser(@Valid @RequestBody UserDTO userDTO) {
        // ...
    }
}

2. 使用Spring REST Docs生成文档

@SpringBootTest
@AutoConfigureRestDocs
class UserApiDocumentation {
    
    @Test
    void getUserExample() throws Exception {
        mockMvc.perform(get("/users/1").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andDo(document("get-user",
                    pathParameters(
                        parameterWithName("id").description("用户ID")
                    ),
                    responseFields(
                        fieldWithPath("id").description("用户ID"),
                        fieldWithPath("username").description("用户名"),
                        fieldWithPath("email").description("邮箱")
                    )
                ));
    }
}

(五) API安全和性能优化

1. 安全措施

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/**").authenticated()
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

// 接口限流
@RestController
@Slf4j
public class RateLimitedController {
    
    private final RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个请求
    
    @GetMapping("/api/limited")
    public ApiResponse<?> limitedApi() {
        if (!rateLimiter.tryAcquire()) {
            log.warn("接口限流触发");
            return ApiResponse.error(429, "请求过于频繁");
        }
        // 正常业务逻辑
        return ApiResponse.success("操作成功");
    }
}

2. 性能监控

@Aspect
@Component
@Slf4j
public class ApiPerformanceAspect {
    
    @Around("@within(org.springframework.web.bind.annotation.RestController)")
    public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        
        try {
            Object result = joinPoint.proceed();
            long costTime = System.currentTimeMillis() - startTime;
            
            // 记录慢查询
            if (costTime > 1000) {
                log.warn("API执行缓慢: {}, 耗时: {}ms", methodName, costTime);
            }
            
            // 推送到监控系统
            Metrics.counter("api.cost", "method", methodName).increment(costTime);
            
            return result;
        } catch (Exception e) {
            Metrics.counter("api.error", "method", methodName).increment();
            throw e;
        }
    }
}

​总结:​​ 良好的API设计是前后端高效协作的基础。通过统一的规范、完善的文档、严格的校验和全面的监控,可以构建出健壮、易用、可维护的API体系。API设计不仅仅是技术实现,更是工程规范和团队协作的体现。

版权声明 ▶ 本网站名称:黄磊的博客
▶ 本文标题:API设计实战:从RESTful规范到前后端协作的最佳实践
▶ 本文链接:https://www.huangleicole.com/backend-related/35.html
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!

如果觉得我的文章对你有用,请随意赞赏