CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
API 设计实战入门/课时/API 风格
RawBlame
课时目录12 节

API 设计实战入门

  1. 01API 基础
  2. 02HTTP 协议层
  3. 03REST 资源设计
  4. 04错误处理
  5. 05认证
  6. 06授权模型
  7. 07API 安全
  8. 08API 风格
  9. 09架构与集成
  10. 10测试
  11. 11可观测与合规
  12. 12文档与工具

API 风格

GraphQL、gRPC 与 WebSocket。

30 分钟

API 风格

这一课你将能做到

  1. 对比 REST、GraphQL、gRPC、WebSocket 四种 API 风格的优缺点,并给出选型建议
  2. 用 GraphQL 实现一个灵活查询的课程接口,理解 Schema 和 Resolver
  3. 用 WebSocket 实现实时通知,理解长连接与轮询的区别

为什么要学

REST 不是银弹。当「前端需要的数据嵌套很深」「移动端要省流量」「微服务之间要高性能通信」「需要实时推送」时,REST 要么啰嗦要么做不到。知道每种风格的适用场景,才能在该用 REST 时用 REST,该换技术时敢换。

先建立图像

四种风格对比:

┌──────────┬─────────────┬─────────────┬─────────────┬─────────────┐
│          │    REST     │   GraphQL   │    gRPC     │  WebSocket  │
├──────────┼─────────────┼─────────────┼─────────────┼─────────────┤
│ 协议      │ HTTP/1.1    │ HTTP/1.1    │ HTTP/2      │ TCP 长连接  │
│ 数据格式  │ JSON        │ JSON        │ Protobuf    │ 自定义      │
│ 契约      │ 弱(靠文档) │ 强(Schema)│ 强(.proto)│ 弱          │
│ 查询方式  │ 固定端点    │ 客户端指定  │ 固定方法    │ 事件驱动    │
│ 实时性    │ 轮询/SSE    │ 轮询/订阅   │ 流式        │ 原生实时    │
│ 适用场景  │ 通用 CRUD   │ 复杂查询    │ 微服务内部  │ 实时通信    │
└──────────┴─────────────┴─────────────┴─────────────┴─────────────┘

跟着做

步骤 1:REST 的局限

场景:前端需要「课程详情 + 讲师信息 + 最新 5 条评价」

REST 方案(3 次请求):

curl /api/lessons/123
curl /api/instructors/456
curl /api/lessons/123/reviews?limit=5

或者做一个聚合端点(后端耦合):

curl /api/lessons/123/detail

问题:要么多次请求(慢),要么后端为每个前端页面定制接口(耦合)。

步骤 2:GraphQL 方案

# Schema 定义
type Lesson {
    id: ID!
    title: String!
    content: String!
    instructor: Instructor!
    reviews(first: Int): [Review!]!
}

type Instructor {
    id: ID!
    name: String!
    avatar: String
}

type Review {
    id: ID!
    content: String!
    rating: Int!
    author: User!
}

type Query {
    lesson(id: ID!): Lesson
}
# 客户端查询:一次请求拿所有数据
query {
    lesson(id: "123") {
        title
        content
        instructor {
            name
            avatar
        }
        reviews(first: 5) {
            content
            rating
            author {
                name
            }
        }
    }
}
// Spring Boot + GraphQL
@Controller
public class LessonGraphQLController {

    @QueryMapping
    public Lesson lesson(@Argument String id) {
        return lessonService.findById(Long.parseLong(id));
    }

    @SchemaMapping
    public Instructor instructor(Lesson lesson) {
        return instructorService.findById(lesson.getInstructorId());
    }

    @SchemaMapping
    public List<Review> reviews(Lesson lesson, @Argument Integer first) {
        return reviewService.findByLessonId(lesson.getId(), first);
    }
}

步骤 3:gRPC 方案

// lesson.proto
syntax = "proto3";

service LessonService {
    rpc GetLesson(LessonRequest) returns (LessonResponse);
    rpc ListLessons(ListLessonsRequest) returns (stream LessonResponse);
}

message LessonRequest {
    int64 id = 1;
}

message LessonResponse {
    int64 id = 1;
    string title = 2;
    string content = 3;
}
// 服务端
@GrpcService
public class LessonGrpcService extends LessonServiceGrpc.LessonServiceImplBase {

    @Override
    public void getLesson(LessonRequest request, StreamObserver<LessonResponse> responseObserver) {
        Lesson lesson = lessonService.findById(request.getId());
        LessonResponse response = LessonResponse.newBuilder()
                .setId(lesson.getId())
                .setTitle(lesson.getTitle())
                .setContent(lesson.getContent())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

gRPC 优势:HTTP/2 多路复用、Protobuf 二进制序列化(比 JSON 小 3-10 倍)、强类型契约。适合微服务之间的高频调用。

步骤 4:WebSocket 实时通知

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new NotificationHandler(), "/ws/notifications")
                .setAllowedOrigins("*");
    }
}

@Component
public class NotificationHandler extends TextWebSocketHandler {

    private static final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String userId = getUserId(session);
        sessions.put(userId, session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 处理客户端消息
    }

    public void sendNotification(String userId, String content) {
        WebSocketSession session = sessions.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(content));
            } catch (IOException e) {
                sessions.remove(userId);
            }
        }
    }
}
// 前端
const ws = new WebSocket('ws://localhost:8080/ws/notifications');
ws.onmessage = (event) => {
    const notification = JSON.parse(event.data);
    showToast(notification.content);
};

讲清楚要点

  1. REST 是默认选择:团队熟悉、生态成熟、缓存友好、调试方便。除非有明确痛点,不要盲目换 GraphQL 或 gRPC。
  2. GraphQL 解决「数据聚合」和「版本演进」:客户端决定要什么数据,一个请求替代多个 REST 调用;新增字段不需要改 API 版本。但缓存复杂、权限控制难、N+1 查询要手动优化。
  3. gRPC 是微服务内部的王者:Protobuf 高效、HTTP/2 流式、代码生成完善。但不适合浏览器直接调用(需要 grpc-web),调试不如 REST 直观。
  4. WebSocket 是实时场景的唯一解:聊天、协作编辑、实时行情。但连接管理复杂(心跳、重连、消息确认),水平扩展需要 Redis Pub/Sub 或消息队列做广播。
  5. 混合使用是常态:对外公开 API 用 REST,复杂数据聚合用 GraphQL,微服务内部用 gRPC,实时通知用 WebSocket。没有一种风格能包打天下。

你可能会懵的地方

  • GraphQL 会不会取代 REST? 不会。GraphQL 适合「前端驱动」的场景(如 Facebook 的 News Feed),REST 适合「后端驱动」的开放 API(如 Stripe、GitHub)。两者会长期共存。

  • gRPC 和 REST 性能差多少? Protobuf 序列化比 JSON 快 5-10 倍,体积小 3-10 倍;HTTP/2 多路复用减少连接开销。内网高频调用(如每秒上万次)gRPC 优势明显,低频外部调用 REST 足够。

  • WebSocket 和 SSE(Server-Sent Events)怎么选? SSE 是单向(服务器→客户端),基于 HTTP,自动重连,适合「服务器推消息」场景(如股票行情);WebSocket 是双向,需要额外协议处理,适合「双向交互」(如聊天)。SSE 更简单,优先用 SSE。

  • GraphQL 怎么做权限控制? 字段级权限:在 Resolver 里判断当前用户能否访问该字段;查询深度限制:防止恶意嵌套查询;查询成本分析:限制单次查询的复杂度。

  • tRPC 是什么? tRPC 是 TypeScript 生态的 RPC 方案,类型安全从后端到前端。适合全栈 TypeScript 项目,但生态和跨语言能力不如 gRPC。

错法对照

错法问题正解
为了用 GraphQL 而用 GraphQL团队学习成本高,简单 CRUD 用 REST 更快明确痛点(多次请求、版本演进)再引入
gRPC 直接暴露给浏览器浏览器不支持 HTTP/2 trailers用 grpc-web 或网关转 REST
WebSocket 不做心跳检测代理/NAT 超时断开,客户端不知道定时 ping/pong,30-60 秒一次
GraphQL 不做深度限制恶意嵌套查询拖垮数据库限制查询深度(如 10 层)和复杂度

卡点急救

  1. GraphQL N+1 问题:DataLoader 批量加载。比如 10 门课程各查讲师,DataLoader 会合并成一次 SQL WHERE id IN (...)。
  2. WebSocket 连接数瓶颈:单机支撑连接数有限(约 6 万)。用 Redis 做 Session 共享,或引入专门的长连接网关(如 Socket.IO、Netty)。
  3. gRPC 调试困难:用 BloomRPC 或 Postman(支持 gRPC)测试;开启 gRPC 反射(Reflection)服务,让工具能动态获取 proto。

面试常问

  • REST、GraphQL、gRPC 怎么选? 对外公开 API、简单 CRUD → REST;复杂数据聚合、前端驱动 → GraphQL;微服务内部高频调用 → gRPC;实时双向通信 → WebSocket。能讲出每种风格的 2-3 个优缺点。

  • GraphQL 的 N+1 问题怎么解决? DataLoader 批量加载、缓存。能写出 DataLoader 的使用代码。

  • WebSocket 怎么做水平扩展? 多台服务器共享 Session(Redis),消息广播走消息队列(Kafka/RabbitMQ)。能画出架构图。

自测三问

  1. 设计一个「实时协作代码编辑器」的 API,选什么风格?为什么?
  2. 如果 GraphQL 查询导致数据库 CPU 100%,可能是什么原因?怎么排查?
  3. 为什么 gRPC 不适合直接暴露给前端?

小结一句话

API 风格没有银弹:REST 通用、GraphQL 灵活、gRPC 高效、WebSocket 实时;按场景选型,混合使用是常态。

延伸

  • GraphQL 官方文档
  • gRPC 官方文档
  • WebSocket MDN
  • 下一课:架构与集成——API 网关、BFF 与微服务通信。
加载本节练习…
← 上一课API 安全下一课 →架构与集成