以下有一些问题:你是如何使用 Google、Apple 或 Microsoft 账号登录应用的?如何使用 Paystack 或 PayPal 进行在线支付?像 Facebook 和 Instagram 这样的应用是如何共享信息和通知的?

答案是:它们使用 API。这些是推动移动和 Web 开发以及各种应用程序(包括云服务、物联网设备、桌面软件等)的强大工具。

API 实现了应用之间的通信,促进了数据交换和验证。

在本文中,你将了解 API 的所有内容:不同类型、它们的架构以及不同架构之间的权衡。

我们将涵盖以下内容:

这篇文章非常适合于网络和移动开发初学者,以及希望简要了解 API 及其如何运作的开发者。

什么是 API?

API 代表应用程序编程接口。它是一组规则和协议,让不同的软件系统相互通信。API 定义了应用程序如何请求服务和交换数据,充当客户端和服务器之间的明确契约。

API 将复杂的代码简化为简单的命令,让开发人员可以连接系统并使用内置功能,而无需了解所有的内部工作原理。

API 如何工作?

想象一下有一家餐馆:顾客(客户端)通过服务员(API)点餐,然后服务员通知厨房(服务器)。厨房备餐后通过服务员传递给顾客。就像服务员一样,API 处理请求和响应,让顾客在无需了解厨房操作细节的情况下享受餐点。

一个更实用的例子是当你在线购买订阅时,你的支付信息通过其支付 API 安全发送至 Paystack。API 是一个中间人,它获取你的请求,验证并处理你的银行支付信息,然后在不直接暴露敏感数据的情况下返回确认给网站。

从技术上讲,客户端向服务器发起请求,指定要获取数据还是执行某个流程。接收并认证该请求后,API 会执行所需操作。随后, API 向客户端发送响应,包括请求的结果(成功或失败)和任何请求的数据元素。

来自 shiksha.com 的图解说明了 API 如何工作。

为什么 API 很重要?

API 在软件开发中至关重要,因为它们使得连接不同的应用程序和服务更加容易。它们允许你整合外部功能,而无需从头开始构建所有内容,通过标准化命令节省时间并降低复杂性。

对于用户来说,API 还可以提高安全性和用户体验。它们作为安全网关,过滤应用和外部服务之间的数据交换,保护敏感信息,同时确保流畅、可靠的互动。

API 类型

API 的类型主要根据其可访问性和使用情况进行分类。API 有四种类型,分别是:

  1. 开放(公共)API

  2. 合作伙伴 API

  3. 内部(私有)API

  4. 组合 API

开放 API

开放 API 是向公众提供的 API。这就鼓励开发者、组织和其他人使用它们来开发应用程序,将其集成到他们的服务中,并加以改进。开放 API 提供了通过互联网访问数据或服务的标准化接口。

一些非常实用的开放 API 包括:

合作伙伴 API

合作伙伴 API 与特定的商业伙伴共享,通常需要身份验证和协议。它们为企业和应用程序执行重要功能。

例如,像 Paystack 这样的支付 API 直接与服务提供商和银行平台通信,以处理产品和服务的支付。

内部 API

内部 API 用于组织内部的通信。它们能够实现集成并简化内部流程。内部团队使用 API 在其应用程序之间访问和共享数据。API 不对公众开放,以确保敏感的业务逻辑保持安全。

一个例子是公司内部 API,它连接其人力资源、工资单和项目管理系统。

复合 API

复合 API 将多个 API 调用组合为一个请求。在微服务架构中,它们非常重要,因为单个操作可能需要从多个服务中获取数据。一次 API 调用会触发对多个底层 API 的请求,然后复合 API 会组合这些响应并返回一个统一的结果。

例如,一个电子商务平台可能会使用复合 API 来一次性获取产品详情、定价和库存信息,减少延迟并简化集成过程。

API 架构类型

根据使用场景、可扩展性、安全性和可访问性,API 的结构各有不同。构建 API 的方式有多种,但我们将仅关注Web开发中最为流行的架构风格,它们包括:

  1. REST

  2. SOAP

  3. GraphQL

  4. gRPC

REST API

表述性状态转移(REST)是一种使用 HTTP 方法(POST、GET、PUT、DELETE)在基于资源的 URI 上执行 CRUD(创建、读取、更新、删除)操作的架构风格。

REST API 使用像 Express.js(Node.js)、Django/Flask(Python)、Spring Boot(Java)这样的框架构建。

关键组件

  1. 资源和端点:

    • API 公开的实体可以包括任何内容:用户、产品、文档等。

    • 每个资源由唯一的 URI(统一资源标识符)标识。

  2. HTTP 方法:

    • GET: 获取资源。

    • POST: 创建新资源。

    • PUT: 更新现有资源。

    • DELETE: 删除资源。

    • PATCH: 部分更新现有资源。

  3. 数据表示:

    • 资源可以有多种表示形式(例如,JSON,XML)。

    • API以请求的表示形式响应,允许对数据进行结构化和解析。

  4. HTTP 标头和查询参数:

    • HTTP 标头提供有关请求或响应的附加信息。

    • 它们可以用于身份验证、内容协商和其他用途。

  5. 无状态:

    • 客户端到服务器的每个请求都必须包含理解和处理请求所需的所有信息。

    • 服务器在请求之间不存储任何客户端状态。

其他显著的组件包括可缓存性、HTTP 状态和 HATEOAS。这些组件共同定义了 RESTful 系统的结构和行为,支持客户端与服务器之间的无缝高效通信。

操作概述

REST API 通过唯一的 URI 公开资源,并允许客户端使用 HTTP 方法(如 GET、POST、PUT、DELETE 和 PATCH)执行操作。客户端可以请求各种格式的数据,如 JSON 或 XML,并通过 HTTP 头和查询参数包含附加细节。

每个请求都是无状态的,包含处理所需的所有信息,而不依赖于存储的客户端数据。API 还使用 HTTP 状态码、可缓存性和 HATEOAS 管理响应并指导进一步的交互,确保客户端和服务器之间的无缝高效通信框架。

来自 apisec.ai 的图示,说明 REST API 的流,包括端点、HTTP 方法以及客户端和服务器之间的数据交换。

实际示例和真实案例

为了说明 REST API 在实践中的工作原理,我们以一个允许用户管理图书集合的图书API 为例。我们的示例 API 是使用 NodeJSExpressJS 框架创建的。这里我们不解释这些框架的具体工作原理,因为这超出了本文的范围。所以如果你不了解下面代码的语法,不用担心,只需关注 请求响应 逻辑。

此 API 遵循 REST 原则,使用标准 HTTP 方法执行 CRUD(创建、读取、更新、删除)操作:

const express = require("express"); const bodyParser = require("body-parser");
const app = express(); app.use(bodyParser.json());

const app = express();
app.use(bodyParser.json());

// 虚拟数据库
let books = [
  { id: 1, title: "The Pragmatic Programmer", author: "Andy Hunt" },
  { id: 2, title: "Clean Code", author: "Robert C. Martin" },
];

// 获取所有书籍(客户端请求,服务器响应)
app.get("/books", (req, res) => res.json(books));
// 新增一本书(客户端发送数据,服务器更新数据库)
app.post("/books", (req, res) => {
  const newBook = { id: books.length + 1, ...req.body };
  books.push(newBook);
  res.status(201).json(newBook);
});

// 更新一本书
app.put("/books/:id", (req, res) => {
  const book = books.find((b) => b.id === parseInt(req.params.id));
  if (book) {
    Object.assign(book, req.body);
    res.json(book);
  } else {
    res.status(404).json({ message: "Not found" });
  }
});

// 删除一本书
app.delete("/books/:id", (req, res) => {
  const index = books.findIndex((b) => b.id === parseInt(req.params.id));
  if (index !== -1) {
    books.splice(index, 1);
    res.json({ message: "Deleted" });
  } else {
    res.status(404).json({ message: "Not found" });
  }
});

app.listen(3000, () => console.log("API running on port 3000"));

此代码的操作流程如下:

  • 客户端发送请求:用户(或前端应用)通过使用诸如 GET,POST,PUT 或 DELETE 等 HTTP 方法请求数据。例如:GET /books 请求所有书籍,或 POST /books 将新书提交到数据库。

  • 服务器处理请求:服务器接收请求,查找数据(例如,从数据库或内存数组中查找),并进行处理。

  • 服务器返回响应:服务器返回包含请求数据或确认消息的 JSON 响应。示例如下:

[
  { "id": 1, "title": "The Pragmatic Programmer", "author": "Andy Hunt" },
  { "id": 2, "title": "Clean Code", "author": "Robert C. Martin" }
]
  • 客户端接收并使用数据:前端或其他服务使用 API 响应,并相应地显示或处理数据。

团队将 REST API 用于 Web 服务、移动应用程序和云集成。社交媒体平台获取帖子,电子商务网站检索产品详情。支付网关处理交易,天气应用程序访问实时预测。REST 的简单性和可扩展性使其成为公共和内部 API 的首选。

SOAP APIs

简单对象访问协议(SOAP)使用 XML 进行消息传递,并包括用于安全、事务和错误处理的内置标准。其正式约定由 WSDL(Web 服务描述语言)定义。

这种架构通过 WS-Security(Web Services Security)和事务管理等功能优先考虑安全性和可靠性,使其适合需要严格标准和强大错误处理的复杂企业应用程序。

SOAP APIs 是使用诸如 Apache CXF、.NET WCF 和 JAX-WS(Java)等框架或工具创建的。

关键组件

  1. SOAP envelope(SOAP 封套):

    • 这是 SOAP 消息的根元素,定义了 XML 文档的整体结构。

    • 它包含 SOAP Header(SOAP 标头)和 SOAP Body(SOAP 主体)。

  2. SOAP body(SOAP 主体):

    • 该部分包含在客户端和服务器之间交换的实际数据。

    • 它包括请求或响应消息,这些消息通常被构造为 XML 元素。

  3. WSDL(Web 服务描述语言):

    • 这是一个描述 Web 服务的 XML 文档,包括其操作、消息格式和数据类型。

    • 它充当客户端和服务器之间的合同,概述如何与 API 交互。

  4. SOAP processor(SOAP 处理器):

    • 这是处理 SOAP 消息的软件组件。

    • 它解析 XML 文档,提取相关数据,并执行请求的操作。

另外,还有 SOAP Endpoint(SOAP 端点),即 SOAP 服务可被访问的 URL,以及 XML Schema (XSD),定义了 SOAP 消息的 XML 结构和数据类型。

操作概述

SOAP APIs 通过将数据封装在由 SOAP 封套定义的基于 XML 的结构中运行,该信封包含用于元数据的标头和用于实际请求或响应信息的主体。主体承载交换数据,而 WSDL 文档则作为契约,详细说明服务的操作、消息格式和数据类型。

SOAP 处理器随后解析 XML,提取相关数据,并根据附带的 XML 模式 (XSD) 定义的规则执行请求的操作。与服务的通信通过特定的 SOAP 端点进行,确保了 Web 服务交互的标准化、互操作框架。

来自 muledreamin.com 的图解,说明 SOAP API 的流程,包括 HTTP 传输以及 SOAP 发送方与 SOAP 接收方之间的数据交换。

实际例子和真实用例

为了说明 SOAP APIs 及其实际工作原理,我们考虑一个基于 SOAP 的银行服务 API,提供用于管理账户和交易的安全操作。SOAP APIs 使用 XML 消息传递,确保系统间的安全和结构化通信。创建 SOAP API 和 XML 消息传递超出了本文的范围,因此我们将在此重点讨论请求和响应逻辑。

如何运作:

  • 获取账户信息: 客户端发送一个 XML 请求以获取用户的账户详情:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:bank="http://example.com/bank">
   <soapenv:Header/>
   <soapenv:Body>
      <bank:GetAccountDetails>
         <bank:AccountNumber>123456789</bank:AccountNumber>
      </bank:GetAccountDetails>
   </soapenv:Body>
</soapenv:Envelope>

服务器以包含账户详细信息的 XML 消息进行响应:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Body>
      <GetAccountDetailsResponse>
         <AccountNumber>123456789</AccountNumber>
         <Balance>5000.00</Balance>
         <Currency>USD</Currency>
      </GetAccountDetailsResponse>
   </soapenv:Body>
</soapenv:Envelope>
  • 处理转账: 客户端提交一个带有认证详情的转账请求:

      <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                        xmlns:bank="http://example.com/bank">
         <soapenv:Header/>
         <soapenv:Body>
            <bank:TransferFunds>
               <bank:FromAccount>123456789</bank:FromAccount>
               <bank:ToAccount>987654321</bank:ToAccount>
               <bank:Amount>100.00</bank:Amount>
               <bank:Currency>USD</bank:Currency>
            </bank:TransferFunds>
         </soapenv:Body>
      </soapenv:Envelope>
    

    如果成功,服务器返回一个确认响应:

      <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
         <soapenv:Body>
            <TransferFundsResponse>
               <Status>Success</Status>
               <TransactionID>TXN987654</TransactionID>
            </TransferFundsResponse>
         </soapenv:Body>
      </soapenv:Envelope>
    

银行、医疗保健提供者和政府机构使用 SOAP 提供安全、可靠的 API。金融机构以严格的身份验证处理交易,而医疗系统在遵从法规下交换患者数据。航空公司依赖 SOAP 进行预订和售票,确保跨系统的一致数据完整性。

GraphQL API

GraphQL 是一种由 Facebook 开发的用于 APIs 的查询语言和运行时。它允许客户端在一个请求中精确地请求所需的数据,减少过度获取和不足获取。

关键组件

  1. 模式: 这是 GraphQL API 的核心。它定义了数据的结构,包括对象类型、其字段及其关系。它作为客户端和服务器之间的契约,指定可以查询的数据。

  2. 类型: 这些定义了数据中对象的结构。它们指定每个对象拥有的字段及这些字段的数据类型。

  3. 字段: 这些是可以在一个对象上查询的独立数据片段。

  4. 查询: 这些是客户端请求以检索数据。它们指定客户端希望恢复的字段。

  5. 变更: 这些是客户端请求以修改数据(创建、更新或删除)。

  6. 解析器: 这些是为模式中每个字段获取数据的函数。它们将 GraphQL 模式连接到底层数据源。

  7. 订阅: 这些启用实时更新。客户端可以订阅特定事件,服务器将在事件发生时推送更新。

操作概述

GraphQL 定义了一个指定可用数据类型及其关系的 schema。然后,客户端构建查询或变更,精确请求所需数据字段。GraphQL 服务器处理这些请求,使用解析器从后端源获取数据。

服务器根据模式验证请求,执行解析器,并返回仅包含请求数据的 JSON 响应。客户端可以建立实时更新的订阅,使服务器在数据改变时推送数据。这种方法最小化了过度获取和不足获取,提高了数据检索的效率和灵活性。

来自 kinsta.com 的图解,展示了 GraphQL 服务器架构,显示了从服务器传输 GraphQL 查询请求和 JSON 负载的过程。

实际示例和真实用例

让我们通过一个由 GraphQL 驱动的电子商务 API 来实际探讨 GraphQL API 的工作原理。此 API 可以高效地获取产品详情、评论和库存可用性。服务器是使用 NodeJS 和 Apollo Server 创建的。创建服务器超出了本文的范围,因此我们将专注于 Schema(关系数据库是如何构建和可视表示的)及 请求响应 逻辑。

  1. 模式:
# Schema (schema.graphql)

type Product {
  id: ID!
  name: String!
  description: String
  price: Float!
  inventory: Int!
  category: String!
}


```markdown
type Mutation {
  createProduct(name: String!, description: String, price: Float!, inventory: Int!, category: String!): Product!
  updateProductInventory(id: ID!, inventory: Int!): Product!
}

这个模式定义了数据类型(ProductQueryMutation)并指定了可用的查询(productproducts)和变更操作(createProductupdateProductInventory)。它使用了 GraphQL 类型系统(String、Int、Float、ID、[ ]、!)

  1. 请求和响应

    • 获取产品数据 - 客户端请求特定的产品字段(例如,名称、价格和描述):

      query {
        product(id: "123") {
          name
          price
          description
        }
      }
      

      如果请求成功,服务器仅返回请求的数据:

      {
        "data": {
          "product": {
            "name": "无线耳机",
            "price": 99.99,
            "inStock": true
          }
        }
      }
      
    • 创建一个新产品:

      mutation {
        createProduct(name: "鼠标", price: 30, inventory: 100, category: "电子产品") {
          id
          name
          price
        }
      }
      
    • 更新产品信息:

      mutation {
        updateProduct(id: "123", price: 89.99) {
          name
          price
        }
      }
      

      如果成功,服务器返回更新后的详细信息:

      {
        "data": {
          "updateProduct": {
            "name": "无线耳机",
            "price": 89.99
          }
        }
      }
      

像 Facebook 和 Shopify 这样的公司使用 GraphQL 来实现高效、灵活的 API。电子商务和社交应用程序只提取所需的数据,减少多余的数据获取。移动应用程序优化性能,而分析工具无缝聚合复杂数据。

gRPC APIs

远程过程调用(gRPC)是一种高性能的 RPC 框架,使用 HTTP/2 和协议缓冲序列化结构化数据。它支持同步和异步通信以及流功能。

HTTP/2 是 HTTP 的最新演进,具备二进制分帧、多路复用、标头压缩和服务器推送等令人兴奋的特性,以提高性能和减少延迟。gRPC 充分利用了这些功能,实现快速、高效、同时的通信,使其非常适合微服务和实时应用。

关键组件

  1. 服务定义:在 .proto 文件中定义。它指定了所提供的服务和可用的 RPC 方法,充当客户端和服务器之间的契约。

  2. 消息是使用协议缓冲区定义的数据结构,可以在系统之间高效地序列化和反序列化数据。

  3. 存根:自动生成的客户端和服务器代码,使客户端可以像调用本地方法一样调用远程方法,并使服务器可以实现服务逻辑。

  4. 通道:它们管理客户端和服务器之间的连接,处理底层网络通信。

  5. RPC 方法:gRPC 支持不同类型的调用,包括一元(单请求-响应)、客户端流、服务器流和双向流,每种都适合不同的使用场景。

  6. 拦截器和元数据:这些提供了附加额外功能的机制,例如通过将元数据附加到 RPC 调用来进行身份验证、日志记录和错误处理。

操作概述

gRPC 让开发者能够在 .proto 文件中使用协议缓冲定义服务契约和消息类型,作为可用 RPC 方法的蓝图。代码生成器生成客户端和服务器存根,允许像本地函数一样调用远程过程,同时通道管理基于 HTTP/2 的网络通信。

它支持一元、客户端流、服务器流和双向流以应对不同的数据交换模式。此外,还可以集成拦截器和元数据来进行身份验证和日志记录等任务,确保系统稳健、安全和高效。

实践示例和实际使用案例

让我们考虑一个使用 gRPC 快速在客户端(移动应用)和后端服务之间快速通信的打车应用。gRPC 使用协议缓冲(Protobuf)进行二进制序列化,而不是像 JSON 或 XML 这样的基于文本的格式。这使得网络通信显著更快且更高效。

  1. .proto 文件定义了 API 结构:
syntax = "proto3";

service RideService {
  rpc RequestRide(RideRequest) returns (RideResponse);
  rpc StreamRideUpdates(RideUpdateRequest) returns (stream RideUpdate);
}

message RideRequest {
  string user_id = 1;
  string pickup_location = 2;
  string destination = 3;
}

message RideResponse {
  string ride_id = 1;
  string driver_name = 2;
  string car_model = 3;
}
message RideUpdateRequest {
  string ride_id = 1;
}

当客户端发送一个 RideRequest 时,它会使用 Protobuf 序列化为一个紧凑的二进制格式。这减少了负载大小,加快了传输速度,提高了效率。服务器会在处理之前将其反序列化为一个结构化的对象。

  1. 请求和响应:

    • 请求乘车:客户端点击按钮发送乘车请求,其中包含:

      {
        "user_id": "U123",
        "pickup_location": "Central Park",
        "destination": "Times Square"
      }
      

      服务器响应司机的详细信息:

      {
        "ride_id": "R456",
        "driver_name": "John Doe",
        "car_model": "Toyota Prius"
      }
      

      你可能会好奇为什么请求和响应是以 JSON 格式显示的,因为 gRPC 并不使用像 JSON 和 XML 这样的基于文本的格式。gRPC 使用的压缩二进制流不像 JSON 那样可读,这是一个紧凑且高效的编码格式,需要通过 Protobuf 反序列化来理解。在压缩二进制流格式中,请求或响应看起来像这样:

      08 D2 04 12 0D 43 65 6E 74 72 61 6C 20 50 61 72 6B 1A 0B 54 69 6D 65 73 20 53 71 75 61 72 65
      
    • 实时流更新:一旦分配了乘车,服务器就会向客户端流式传输实时更新:

      {
        "ride_id": "R456",
        "status": "Driver on the way",
        "driver_location": "5th Avenue"
      }
      

公司使用 gRPC 来满足高性能、实时应用程序中对高效服务通信的需求。像 Google、Netflix 和 Dropbox 这样的科技巨头使用 gRPC 来实现可扩展的微服务。共享乘车应用程序会流式传输实时司机位置,而金融科技平台则管理安全、低延迟的交易。物联网系统和人工智能应用依赖 gRPC 来进行实时数据交换和高效交互。

如何选择API架构

选择一个 API 架构需要根据项目的具体需求平衡性能、可扩展性、易用性和安全性等多个因素。

REST 因其简单性和无状态设计而闻名,这有助于可扩展性和易用性,但其安全性主要依赖于外部措施,如 HTTPS 和适当的认证机制。

SOAP 尽管更复杂,但提供了强大的内置安全标准(如 WS-Security)和可靠的事务支持,使其适合于企业环境。

GraphQL 通过只允许客户端请求所需的数据,提供了高效的数据获取和高性能,但可能需要额外的安全措施,比如查询深度限制和服务器端的恰当认证。

gRPC 提供了卓越的性能,非常适合实时数据需求的微服务。它利用 HTTP/2 和 TLS 来实现安全、高效的通信,尽管它需要较长的学习曲线。

下表总结了这些架构之间的特点和权衡:

特点 REST SOAP GraphQL gRPC
性能 中等(可能存在数据过度获取)
可扩展性 中等 非常高(适用于微服务和实时数据)
易用性 简单且广泛采用 复杂 对于客户端来说直观(服务器端可能复杂) 学习曲线陡峭
安全性 依赖于外部机制(HTTPS、OAuth 等) 通过 WS-Security 和正式合同提供强大的内置安全性 需要额外的措施(查询验证、速率限制) 高安全性,内置 TLS 支持和强大的认证协议

结论和未来趋势

API 已成为现代软件开发中的基本组成部分,促进了不同应用程序之间的无缝通信和数据交换。它们的影响是不可否认的,从推动创新的公共 API 到简化内部流程的私有 API。

理解 REST、SOAP、GraphQL 和 gRPC 等不同的 API 架构,使开发人员能够根据特定需求选择最佳方法,在性能、可扩展性和易用性之间取得平衡。

展望未来,API 领域将迎来令人兴奋的变化。随着 AI 驱动的 API、去中心化架构和改进的安全措施的出现,我们将看到新的方式来构建和交互软件。API 标准的持续演变和低代码/无代码平台的增长使 API 开发变得更容易为所有人所用。