RESTful API 面试详解

一、RESTful API 是什么?

一句话定义

RESTful API 是一种基于 HTTP 协议的软件架构风格,它使用标准 HTTP 方法(GET、POST、PUT、DELETE 等)对资源进行操作,让 Web 服务的设计更加简洁、统一。

核心原则(REST 六大约束)

  1. 客户端-服务器分离:前后端分离,独立演化

  2. 无状态:每次请求都包含所有必要信息,服务器不保存会话状态

  3. 可缓存:响应必须明确是否可缓存

  4. 统一接口:使用标准 HTTP 方法和状态码

  5. 分层系统:客户端不知道是与服务器还是中间件通信

  6. 按需代码(可选):服务器可返回可执行代码(如 JavaScript)

RESTful 的设计特征

复制代码
# 传统 API(非 RESTful)
GET /getUser?id=1
POST /updateUser
GET /deleteUser?id=1

# RESTful API(资源导向)
GET    /users/1          # 获取用户1
PUT    /users/1          # 更新用户1
DELETE /users/1          # 删除用户1
POST   /users            # 创建新用户

实际示例

复制代码
// RESTful 风格的控制器
@RestController
@RequestMapping("/api/products")
public class ProductController {
    
    // GET    /api/products      - 获取所有产品
    @GetMapping
    public List<Product> getAllProducts() {
        return productService.findAll();
    }
    
    // GET    /api/products/{id} - 获取单个产品
    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.findById(id);
    }
    
    // POST   /api/products      - 创建产品
    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        return productService.save(product);
    }
    
    // PUT    /api/products/{id} - 更新产品
    @PutMapping("/{id}")
    public Product updateProduct(@PathVariable Long id, 
                                 @RequestBody Product product) {
        return productService.update(id, product);
    }
    
    // DELETE /api/products/{id} - 删除产品
    @DeleteMapping("/{id}")
    public void deleteProduct(@PathVariable Long id) {
        productService.delete(id);
    }
}

二、GET 和 POST 的区别

对比表格

特性 GET POST
语义 获取数据(安全、幂等) 提交数据(不安全、不幂等)
参数位置 URL 查询字符串 请求体(Body)
参数可见性 明文显示在 URL 隐藏在请求体中
数据长度 有限制(URL长度限制) 无限制(理论上)
安全性 较低(参数在URL中) 较高(参数在Body中)
缓存 可被缓存 不可缓存
浏览器历史 保留记录 不保留记录
后退/刷新 无害 会重新提交(浏览器提示)
幂等性 幂等(多次请求结果相同) 不幂等(每次可能产生不同结果)
书签 可收藏为书签 不可收藏

详细解释

1. 语义区别(最重要)
  • GET获取资源,不应改变服务器状态

    复制代码
    GET /api/users/123
    GET /api/products?category=electronics&page=2
  • POST创建新资源,改变服务器状态

    复制代码
    POST /api/users
    Content-Type: application/json
    
    {"name": "张三", "email": "zhangsan@example.com"}
2. 幂等性
  • GET 是幂等的:执行 1 次和 N 次效果相同

    复制代码
    # 无论请求多少次,都不会创建新用户
    GET /api/users/1  ✅ 安全
    GET /api/users/1  ✅ 安全
    GET /api/users/1  ✅ 安全
  • POST 不是幂等的:每次可能产生不同结果

    复制代码
    # 每次请求都会创建新用户
    POST /api/users  → 创建用户A
    POST /api/users  → 创建用户B
    POST /api/users  → 创建用户C
3. 安全性
  • GET 是安全的:不应改变数据(只读)

  • POST 不安全:会改变数据

4. 参数传递
复制代码
# GET - 参数在URL中
GET /api/search?keyword=java&page=1&size=20

# POST - 参数在Body中
POST /api/login
Content-Type: application/x-www-form-urlencoded

username=zhangsan&password=123456

# POST with JSON
POST /api/users
Content-Type: application/json

{
  "name": "张三",
  "age": 25,
  "email": "zhangsan@example.com"
}
5. 缓存机制
复制代码
// GET 请求可以被缓存
@GetMapping("/products/{id}")
@Cacheable(value = "products", key = "#id")  // Spring Cache
public Product getProduct(@PathVariable Long id) {
    return productService.findById(id);
}

// POST 请求不应该被缓存
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderRequest request) {
    return orderService.create(request);  // 每次都执行
}
6. 实际使用场景
复制代码
// ✅ GET 的正确使用场景
@GetMapping("/users")                    // 获取用户列表
@GetMapping("/users/{id}")              // 获取单个用户
@GetMapping("/users/{id}/orders")       // 获取用户的订单
@GetMapping("/search")                  // 搜索(参数少时)

// ✅ POST 的正确使用场景  
@PostMapping("/users")                  // 创建用户
@PostMapping("/login")                  // 用户登录
@PostMapping("/orders")                 // 下订单
@PostMapping("/search")                 // 复杂搜索(参数多时)

三、面试回答模板

问题1:RESTful API 是什么?

标准回答

"RESTful API 是一种基于 HTTP 协议的架构风格,核心思想是把一切视为资源,用统一的接口进行操作。它有六个约束原则,最重要的是无状态和统一接口。"

"具体来说,它用 URL 表示资源,用 HTTP 方法表示操作:GET 获取、POST 创建、PUT 更新、DELETE 删除。比如用户资源:GET /users 获取列表,POST /users 创建用户,GET /users/1 获取用户1,PUT /users/1 更新用户1,DELETE /users/1 删除用户1。"

加分回答

"在我们项目中,RESTful API 带来了三个好处:第一是前后端解耦,前端只需要知道接口规范;第二是可缓存性提升性能;第三是自描述性,看 URL 就知道操作什么资源。"

问题2:GET 和 POST 有什么区别?

标准回答

"GET 和 POST 有五大核心区别:

  1. 语义不同:GET 获取数据,POST 提交数据

  2. 参数位置:GET 在 URL 中,POST 在请求体里

  3. 安全性:GET 是安全的(只读),POST 不安全(会改数据)

  4. 幂等性:GET 是幂等的,POST 不是幂等的

  5. 可缓存性:GET 可缓存,POST 不可缓存"

"实际开发中,获取数据用 GET,创建数据用 POST。敏感数据如密码一定要用 POST,避免在 URL 中暴露。"

场景化回答

"比如电商网站,搜索商品用 GET:/api/products?q=手机&page=1,参数在 URL 中,可以收藏链接。下单用 POST:/api/orders,订单数据在请求体里,避免刷新重复下单。"

问题3:什么时候用 GET?什么时候用 POST?

判断标准

  1. 用 GET

    • 获取数据(搜索、列表、详情)

    • 参数少且不敏感

    • 希望结果可缓存、可收藏

    • 幂等操作(多次执行结果相同)

  2. 用 POST

    • 创建新资源(注册、下单)

    • 参数多或包含敏感信息

    • 改变服务器状态的操作

    • 非幂等操作(每次可能不同)

实际示例

复制代码
# ✅ GET 合适场景
GET /api/articles?category=tech      # 获取文章列表
GET /api/users/123                   # 获取用户信息
GET /api/products/search?q=手机      # 搜索商品

# ✅ POST 合适场景  
POST /api/login                      # 用户登录(密码敏感)
POST /api/orders                     # 创建订单(改变状态)
POST /api/files/upload               # 上传文件(数据量大)
POST /api/payments                   # 支付请求(敏感操作)

四、高级话题(加分项)

1. 其他 HTTP 方法

  • PUT:更新整个资源(幂等)

  • PATCH:部分更新资源(不幂等)

  • DELETE:删除资源(幂等)

  • HEAD:获取响应头

  • OPTIONS:获取支持的 HTTP 方法

2. 幂等性实践

复制代码
// PUT 是幂等的
@PutMapping("/users/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
    // 无论调用多少次,结果都一样
    return userService.update(id, user);
}

// PATCH 可能不幂等
@PatchMapping("/users/{id}/balance")
public User addBalance(@PathVariable Long id, @RequestParam BigDecimal amount) {
    // 每次调用余额增加,不幂等
    return userService.addBalance(id, amount);
}

3. RESTful 最佳实践

  • 使用复数名词:/users而不是 /user

  • 使用嵌套资源表示关系:/users/1/orders

  • 使用合适的状态码:200 OK、201 Created、404 Not Found

  • 版本控制:/api/v1/users或 Header 中指定版本

  • 过滤、分页、排序:/users?active=true&page=2&size=20&sort=name,desc

一句话总结RESTful 是用 URL 定位资源,用 HTTP 方法描述操作;GET 是安全的只读操作,POST 是创建资源的非安全操作。

相关推荐
2501_901147838 分钟前
面试必看:优势洗牌
笔记·学习·算法·面试·职场和发展
李日灐9 分钟前
C++进阶必备:红黑树从 0 到 1: 手撕底层,带你搞懂平衡二叉树的平衡逻辑与黑高检验
开发语言·数据结构·c++·后端·面试·红黑树·自平衡二叉搜索树
qq_2975746744 分钟前
【实战】POI 实现 Excel 多级表头导出(含合并单元格完整方案)
java·spring boot·后端·excel
Bella的成长园地1 小时前
为什么c++中的条件变量的 wait() 函数需要配合while 循环或谓词?
c++·面试
郝学胜-神的一滴1 小时前
超越Spring的Summer(一): PackageScanner 类实现原理详解
java·服务器·开发语言·后端·spring·软件构建
Tony Bai1 小时前
“Go 2,请不要发生!”:如果 Go 变成了“缝合怪”,你还会爱它吗?
开发语言·后端·golang
Victor3561 小时前
Hibernate(91)如何在数据库回归测试中使用Hibernate?
后端
Victor3561 小时前
MongoDB(1)什么是MongoDB?
后端
Victor3568 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端
Victor3568 小时前
Hibernate(89)如何在压力测试中使用Hibernate?
后端