资讯动态

API 设计最佳实践:构建优雅的 RESTful 接口

发布时间:2026/8/5 9:35:30 来源:尧图企业网站定制
API 设计最佳实践构建优雅的 RESTful 接口别叫我大神叫我 Alex 就好。好的 API 设计就像好的 UI 设计简洁、直观、易于使用。一、RESTful API 设计原则1.1 URL 设计规范# 资源命名名词复数 GET /api/v1/users # 获取用户列表 GET /api/v1/users/{id} # 获取单个用户 POST /api/v1/users # 创建用户 PUT /api/v1/users/{id} # 全量更新用户 PATCH /api/v1/users/{id} # 部分更新用户 DELETE /api/v1/users/{id} # 删除用户 # 嵌套资源 GET /api/v1/users/{id}/orders # 获取用户订单 GET /api/v1/users/{id}/orders/{orderId} # 获取用户特定订单 POST /api/v1/users/{id}/orders # 为用户创建订单 # 过滤、排序、分页 GET /api/v1/products?categoryelectronicsprice_min100price_max500 GET /api/v1/products?sort-created_atpage2per_page20 GET /api/v1/products?fieldsid,name,price # 字段筛选 # 搜索 GET /api/v1/products?qiphonehighlighttrue1.2 HTTP 状态码规范RestController RequestMapping(/api/v1) public class UserController { GetMapping(/users/{id}) public ResponseEntityUser getUser(PathVariable Long id) { return userService.findById(id) .map(user - ResponseEntity.ok(user)) // 200 OK .orElse(ResponseEntity.notFound().build()); // 404 Not Found } PostMapping(/users) public ResponseEntityUser createUser(RequestBody Valid UserDTO dto) { User user userService.create(dto); URI location ServletUriComponentsBuilder .fromCurrentRequest() .path(/{id}) .buildAndExpand(user.getId()) .toUri(); return ResponseEntity.created(location).body(user); // 201 Created } DeleteMapping(/users/{id}) public ResponseEntityVoid deleteUser(PathVariable Long id) { userService.delete(id); return ResponseEntity.noContent().build(); // 204 No Content } PutMapping(/users/{id}) public ResponseEntityUser updateUser(PathVariable Long id, RequestBody Valid UserDTO dto) { User user userService.update(id, dto); return ResponseEntity.ok(user); // 200 OK } }二、请求响应规范2.1 统一响应格式Data Builder public class ApiResponseT { private int code; private String message; private T data; private Long timestamp; private String requestId; public static T ApiResponseT success(T data) { return ApiResponse.Tbuilder() .code(200) .message(success) .data(data) .timestamp(System.currentTimeMillis()) .requestId(MDC.get(requestId)) .build(); } public static T ApiResponseT error(int code, String message) { return ApiResponse.Tbuilder() .code(code) .message(message) .timestamp(System.currentTimeMillis()) .requestId(MDC.get(requestId)) .build(); } } // 分页响应 Data Builder public class PageResponseT { private ListT items; private Pagination pagination; Data Builder public static class Pagination { private int page; private int perPage; private long total; private int totalPages; private boolean hasNext; private boolean hasPrev; } public static T PageResponseT of(PageT page) { return PageResponse.Tbuilder() .items(page.getContent()) .pagination(Pagination.builder() .page(page.getNumber() 1) .perPage(page.getSize()) .total(page.getTotalElements()) .totalPages(page.getTotalPages()) .hasNext(page.hasNext()) .hasPrev(page.hasPrevious()) .build()) .build(); } }2.2 全局异常处理RestControllerAdvice public class GlobalExceptionHandler { private static final Logger log LoggerFactory.getLogger(GlobalExceptionHandler.class); ExceptionHandler(BusinessException.class) public ResponseEntityApiResponseVoid handleBusinessException(BusinessException e) { log.warn(Business exception: {}, e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(ApiResponse.error(e.getCode(), e.getMessage())); } ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntityApiResponseMapString, String handleValidationException( MethodArgumentNotValidException e) { MapString, String errors new HashMap(); e.getBindingResult().getFieldErrors().forEach(error - errors.put(error.getField(), error.getDefaultMessage()) ); ApiResponseMapString, String response ApiResponse.MapString, Stringbuilder() .code(400) .message(Validation failed) .data(errors) .timestamp(System.currentTimeMillis()) .build(); return ResponseEntity.badRequest().body(response); } ExceptionHandler(Exception.class) public ResponseEntityApiResponseVoid handleException(Exception e) { log.error(Unexpected error, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(ApiResponse.error(500, Internal server error)); } }三、版本控制策略3.1 URL 版本控制RestController RequestMapping(/api/v1/users) public class UserControllerV1 { // V1 实现 } RestController RequestMapping(/api/v2/users) public class UserControllerV2 { // V2 实现支持更多字段 }3.2 Header 版本控制Configuration public class WebConfig implements WebMvcConfigurer { Override public void configureContentNegotiation(ContentNegotiationConfigurer configurer) { configurer.parameterName(version) .favorParameter(true) .mediaType(v1, MediaType.valueOf(application/vnd.api.v1json)) .mediaType(v2, MediaType.valueOf(application/vnd.api.v2json)); } } RestController RequestMapping(/api/users) public class UserController { GetMapping(produces application/vnd.api.v1json) public UserV1 getUserV1(PathVariable Long id) { return userService.getUserV1(id); } GetMapping(produces application/vnd.api.v2json) public UserV2 getUserV2(PathVariable Long id) { return userService.getUserV2(id); } }四、安全性设计4.1 API 认证授权Configuration EnableWebSecurity public class ApiSecurityConfig { Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(csrf - csrf.disable()) .sessionManagement(session - session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth - auth .requestMatchers(/api/v1/public/**).permitAll() .requestMatchers(/api/v1/admin/**).hasRole(ADMIN) .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 - oauth2 .jwt(jwt - jwt.jwtDecoder(jwtDecoder())) ); return http.build(); } Bean public JwtDecoder jwtDecoder() { return ReactiveJwtDecoders.fromIssuerLocation(https://auth-server); } } // API Key 认证 Component public class ApiKeyAuthFilter extends OncePerRequestFilter { Autowired private ApiKeyRepository apiKeyRepository; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String apiKey request.getHeader(X-API-Key); if (apiKey ! null apiKeyRepository.isValid(apiKey)) { ApiKey key apiKeyRepository.findByKey(apiKey); Authentication auth new ApiKeyAuthenticationToken( key.getClientId(), key.getAuthorities() ); SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); } }4.2 限流与防护Component public class RateLimitingFilter extends OncePerRequestFilter { Autowired private RedisTemplateString, String redisTemplate; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String clientId getClientId(request); String key rate_limit: clientId; Long current redisTemplate.opsForValue().increment(key); if (current 1) { redisTemplate.expire(key, 1, TimeUnit.MINUTES); } if (current 100) { // 每分钟 100 请求 response.setStatus(429); response.getWriter().write({\error\:\Rate limit exceeded\}); return; } // 添加限流头 response.addHeader(X-RateLimit-Limit, 100); response.addHeader(X-RateLimit-Remaining, String.valueOf(100 - current)); filterChain.doFilter(request, response); } }五、文档与测试5.1 OpenAPI 文档Configuration public class OpenApiConfig { Bean public OpenAPI customOpenAPI() { return new OpenAPI() .info(new Info() .title(My API) .version(1.0.0) .description(API Documentation) .contact(new Contact() .name(Support Team) .email(supportexample.com)) .license(new License() .name(Apache 2.0) .url(https://www.apache.org/licenses/LICENSE-2.0))) .addSecurityItem(new SecurityRequirement().addList(bearerAuth)) .components(new Components() .addSecuritySchemes(bearerAuth, new SecurityScheme() .type(SecurityScheme.Type.HTTP) .scheme(bearer) .bearerFormat(JWT))); } } RestController RequestMapping(/api/v1/users) Tag(name User Management, description Operations for user management) public class UserController { Operation(summary Get user by ID, description Returns a single user) ApiResponses({ ApiResponse(responseCode 200, description Found the user), ApiResponse(responseCode 404, description User not found) }) GetMapping(/{id}) public ResponseEntityUser getUser( Parameter(description User ID) PathVariable Long id) { // implementation } }六、总结好的 API 设计需要关注一致性统一的命名和规范可预测性符合 RESTful 原则安全性认证、授权、限流可维护性版本控制、文档完善用户体验清晰的错误信息这其实可以更优雅一点。API 是产品的门面值得花时间精心设计。参考资源RESTful API Design Best PracticesMicrosoft REST API GuidelinesOpenAPI Specification

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价