RESTful API 设计与后端服务开发面试题

一、RESTful 架构核心概念

1. REST 六大原则

  • 客户端-服务器分离:前后端分离架构

  • 无状态:每次请求包含完整信息

  • 可缓存:明确标识可缓存资源

  • 统一接口:HTTP方法标准化操作

  • 分层系统:中间件透明处理

  • 按需代码:可选的客户端扩展

2. REST vs SOAP

复制代码
REST:
  - 基于HTTP
  - 轻量级,易理解
  - JSON/XML格式
  - 性能更好
  
SOAP:
  - 基于XML
  - 协议复杂
  - 自带WS-Security
  - 企业级应用

二、RESTful API 设计规范

1. 资源命名规范

复制代码
// 使用名词复数形式
GET    /api/users          // 获取用户列表
GET    /api/users/{id}     // 获取单个用户
POST   /api/users          // 创建用户
PUT    /api/users/{id}     // 更新用户(全量)
PATCH  /api/users/{id}     // 更新用户(部分)
DELETE /api/users/{id}     // 删除用户
GET    /api/users/{id}/orders  // 获取用户的订单

2. HTTP 方法语义

方法 幂等性 安全性 描述
GET 获取资源
POST 创建资源
PUT 全量更新资源
PATCH 部分更新资源
DELETE 删除资源
HEAD 获取响应头
OPTIONS 获取支持方法

3. 版本管理策略

复制代码
// 方案1:URI路径版本
GET /api/v1/users
GET /api/v2/users

// 方案2:查询参数版本
GET /api/users?version=1

// 方案3:请求头版本
Accept: application/vnd.myapp.v1+json

// 推荐:URI路径版本,简单直观

4. 响应设计规范

复制代码
// 统一响应格式
{
  "code": 200,           // 状态码
  "message": "success",  // 消息
  "data": { ... },       // 数据
  "timestamp": 1631234567
}

// 分页响应
{
  "code": 200,
  "message": "success",
  "data": {
    "items": [...],      // 数据列表
    "total": 100,        // 总记录数
    "page": 1,          // 当前页
    "size": 10,         // 每页大小
    "pages": 10         // 总页数
  }
}

三、HTTP 状态码使用

1. 常见状态码

复制代码
// 2xx 成功
200 OK                    // 请求成功
201 Created               // 创建成功
204 No Content            // 删除成功,无返回体

// 3xx 重定向
301 Moved Permanently     // 永久重定向
302 Found                 // 临时重定向
304 Not Modified          // 资源未修改

// 4xx 客户端错误
400 Bad Request           // 请求格式错误
401 Unauthorized          // 未认证
403 Forbidden             // 无权限
404 Not Found             // 资源不存在
409 Conflict              // 资源冲突
422 Unprocessable Entity  // 参数验证失败

// 5xx 服务器错误
500 Internal Server Error // 服务器内部错误
502 Bad Gateway           // 网关错误
503 Service Unavailable   // 服务不可用

2. 状态码使用场景

复制代码
POST   /api/users  → 201 Created
GET    /api/users/1 → 200 OK
DELETE /api/users/1 → 204 No Content
PUT    /api/users/1 → 200 OK
GET    /api/users/999 → 404 Not Found
POST   /api/users (重复) → 409 Conflict

四、请求参数设计

1. 参数传递方式

复制代码
// 路径参数
GET /api/users/{id}
@PathVariable Long id

// 查询参数
GET /api/users?name=jack&age=20
@RequestParam String name
@RequestParam(required = false) Integer age

// 请求体参数
POST /api/users
@RequestBody User user

// 请求头参数
@RequestHeader("Authorization") String token

// 表单参数
@RequestParam Map<String, String> params

2. 查询参数规范

复制代码
// 过滤
GET /api/users?name=jack&age_gt=18&age_lt=30

// 分页
GET /api/users?page=1&size=10&sort=name,asc&sort=age,desc

// 字段选择
GET /api/users?fields=id,name,email

// 搜索
GET /api/users?q=keyword&searchFields=name,email

五、安全性设计

1. 认证与授权

复制代码
// 1. JWT认证
@PostMapping("/login")
public Result login(@RequestBody LoginRequest request) {
    // 验证用户名密码
    String token = jwtUtil.generateToken(user);
    return Result.success(token);
}

// 2. 接口鉴权
@GetMapping("/admin/users")
@PreAuthorize("hasRole('ADMIN')")
public Result getAdminUsers() {
    // 需要ADMIN角色
}

2. API 安全措施

复制代码
# 防止常见攻击
- 输入验证和清理
- SQL注入防护
- XSS防护
- CSRF Token
- 速率限制
- HTTPS强制
- 参数加密

六、API 文档

1. Swagger/OpenAPI 配置

复制代码
@Configuration
@EnableOpenApi
public class SwaggerConfig {
    
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.OAS_30)
            .apiInfo(apiInfo())
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example"))
            .paths(PathSelectors.any())
            .build();
    }
    
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
            .title("用户管理API")
            .description("用户管理接口文档")
            .version("1.0")
            .build();
    }
}

2. API 文档注解

复制代码
@Api(tags = "用户管理")
@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @ApiOperation("获取用户列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "page", value = "页码", dataType = "int"),
        @ApiImplicitParam(name = "size", value = "每页数量", dataType = "int")
    })
    @GetMapping
    public Result<List<User>> getUsers(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        // 实现
    }
}

七、错误处理

1. 全局异常处理

复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    
    // 业务异常
    @ExceptionHandler(BusinessException.class)
    public Result handleBusinessException(BusinessException e) {
        return Result.error(e.getCode(), e.getMessage());
    }
    
    // 参数验证异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleValidationException(MethodArgumentNotValidException e) {
        String message = e.getBindingResult()
            .getFieldErrors()
            .stream()
            .map(fieldError -> fieldError.getField() + ": " + fieldError.getDefaultMessage())
            .collect(Collectors.joining("; "));
        return Result.error(400, message);
    }
    
    // 未授权异常
    @ExceptionHandler(AccessDeniedException.class)
    public Result handleAccessDeniedException(AccessDeniedException e) {
        return Result.error(403, "无权访问");
    }
}

2. 自定义异常

复制代码
public class BusinessException extends RuntimeException {
    
    private final int code;
    
    public BusinessException(int code, String message) {
        super(message);
        this.code = code;
    }
    
    public int getCode() {
        return code;
    }
}

// 使用
throw new BusinessException(1001, "用户不存在");

八、性能优化

1. 缓存策略

复制代码
// HTTP缓存
@GetMapping("/users/{id}")
@CacheControl(maxAge = 3600)  // 缓存1小时
public ResponseEntity<User> getUser(@PathVariable Long id) {
    User user = userService.getUser(id);
    return ResponseEntity.ok()
            .cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS))
            .eTag(user.getVersion().toString())
            .body(user);
}

2. 分页优化

复制代码
// 避免深度分页
@GetMapping("/users")
public Result<PageResult<User>> getUsers(
        @RequestParam(defaultValue = "1") int page,
        @RequestParam(defaultValue = "10") int size) {
    
    // 使用游标分页
    if (page > 100) {
        throw new BusinessException(400, "分页深度过大");
    }
    
    // 使用覆盖索引
    Page<User> userPage = userRepository.findAll(PageRequest.of(page-1, size));
    return Result.success(PageResult.of(userPage));
}

九、API 测试

1. Postman 测试

复制代码
# 测试用例设计
- 正常场景测试
- 边界值测试
- 异常场景测试
- 性能测试
- 安全性测试

2. 单元测试

复制代码
@SpringBootTest
@AutoConfigureMockMvc
class UserControllerTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void testGetUser() throws Exception {
        // 准备数据
        User user = new User(1L, "张三", "zhangsan@example.com");
        when(userService.getUser(1L)).thenReturn(user);
        
        // 执行请求
        mockMvc.perform(get("/api/users/1")
                .contentType(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.code").value(200))
                .andExpect(jsonPath("$.data.name").value("张三"));
    }
}

十、API 网关与监控

1. 网关功能

复制代码
# 常见网关功能
- 路由转发
- 负载均衡
- 限流熔断
- 身份认证
- 日志监控
- API聚合

2. 监控指标

复制代码
// 重要监控指标
- 请求量(QPS)
- 响应时间(P50, P90, P99)
- 错误率
- 成功率
- 吞吐量
- 系统资源(CPU, 内存)

十一、版本管理策略

1. 版本演进示例

复制代码
// v1版本
@GetMapping("/api/v1/users/{id}")
public UserV1 getUserV1(@PathVariable Long id) {
    // 返回v1数据结构
}

// v2版本 - 向后兼容
@GetMapping("/api/v2/users/{id}")
public UserV2 getUserV2(@PathVariable Long id) {
    // 返回v2数据结构
    // 可以兼容v1客户端
}

// 弃用旧版本
@Deprecated
@GetMapping("/api/v1/users/{id}")
public UserV1 getUserV1(@PathVariable Long id) {
    // 返回弃用信息
}

十二、实际案例设计

1. 电商用户模块 API 设计

复制代码
// 用户模块接口
POST   /api/v1/register        // 注册
POST   /api/v1/login           // 登录
POST   /api/v1/logout          // 登出
GET    /api/v1/users/{id}      // 获取用户信息
PUT    /api/v1/users/{id}      // 更新用户信息
PATCH  /api/v1/users/{id}/password  // 修改密码
GET    /api/v1/users/{id}/orders     // 用户订单
GET    /api/v1/users/{id}/addresses  // 用户地址
POST   /api/v1/users/{id}/addresses  // 添加地址

2. 订单模块 API 设计

复制代码
// 订单模块接口
GET    /api/v1/orders          // 订单列表
POST   /api/v1/orders          // 创建订单
GET    /api/v1/orders/{id}     // 订单详情
PUT    /api/v1/orders/{id}     // 更新订单
DELETE /api/v1/orders/{id}     // 取消订单
POST   /api/v1/orders/{id}/pay // 支付订单
GET    /api/v1/orders/{id}/items  // 订单商品

十三、常见面试题

1. RESTful 设计原则题

复制代码
Q: 设计一个博客系统的API接口
要求:
1. 用户可以发布、修改、删除文章
2. 文章可以被分类、打标签
3. 用户可以评论文章
4. 需要支持分页查询

你的设计方案:

2. 实际问题解决题

复制代码
Q: 如何设计一个支持高并发的短链接生成服务?
考虑点:
1. 短链接生成算法
2. 重定向性能优化
3. 防重复设计
4. 过期机制
5. 统计功能

3. API 设计评审题

复制代码
// 找出以下API设计的问题
GET    /getUserList
POST   /addUser
POST   /updateUser
GET    /deleteUser?id=1
POST   /userLogin
GET    /getOrdersByUserId?userId=1

面试准备要点

  1. 深入理解RESTful设计原则

  2. 掌握HTTP协议和状态码

  3. 熟悉Spring Boot实现RESTful API

  4. 了解API安全性和性能优化

  5. 准备实际项目设计案例

  6. 熟悉API文档和测试工具

  7. 了解微服务架构下的API设计

高频问题

  • RESTful API设计规范

  • HTTP方法的选择依据

  • 如何设计版本管理

  • 如何保证API安全性

  • 如何处理API兼容性

  • 如何设计分页接口

  • 如何进行API性能优化

  • 如何设计错误处理机制

相关推荐
Assby2 小时前
Java开发者学习Go语言:Go开发和Java开发的一些区别
后端·go
吃吃喝喝小朋友2 小时前
Django Admin后台系统
后端·python·django
树獭叔叔2 小时前
检索增强生成(RAG):让大模型突破知识边界
后端·aigc·openai
南囝coding2 小时前
OpenClaw 到底能干什么?可以看看这 60 个真实用例
前端·后端
重庆穿山甲2 小时前
Java开发者的大模型入门:AgentScope Java组件全攻略(二)
前端·后端
Java水解3 小时前
Spring Boot 数据缓存与性能优化
spring boot·后端
我爱娃哈哈3 小时前
SpringBoot + 网关流量染色 + 测试环境隔离:线上流量复制到预发环境,零风险验证
后端
@atweiwei3 小时前
Tokio 深度解析:Rust 异步运行时与 Go 协程对比指南
服务器·网络·后端·golang·rust·内存·所有权