接口开发
API概述
API,全称为 应用程序编程接口(Application Programming Interface),是一组定义了不同软件组件之间如何交互的规则和协议。API 允许不同的软件系统通过预定义的接口进行通信,使得开发者能够利用现有的功能和服务,而无需从头开始构建。这种模块化的设计不仅提高了开发效率,还促进了软件生态系统的扩展和创新。
一个典型的 API 包含以下几个关键组成部分:
- 请求 (Request): 客户端向服务器发送的调用请求,通常包含请求的方法、URL、头信息和参数。
- 响应(Response): 服务器对客户端请求的返回结果,包含状态码、头信息和数据载体。
- 端点(Endpoint): API 提供的具体功能接口的 URL 地址。
- 方法(Method): 定义在特定端点上可以执行的操作,如 GET、POST、PUT、DELETE 等。
- 数据格式(Data Format): 传输数据的格式,常见的有 JSON、XML 等。
接口标准
| 特性 | OpenAI API 风格 | MCP | OpenAPI |
|---|---|---|---|
| 类型 | 商业 API 设计 | 开放通信协议 | 开放 API 描述规范 |
| 是否开放治理 | ❌(OpenAI 控制) | ✅(社区驱动) | ✅(Linux 基金会) |
| 是否可自由实现 | ⚠️(法律风险模糊) | ✅(MIT 许可) | ✅ |
| 是否有正式规范文档 | ✅(但非标准组织发布) | ✅(mcp.spec) | ✅(openapis.org) |
| 目标 | 调用 LLM | LLM 与工具通信 | 描述 RESTful API |
开发框架
任何一家公司都可以定义符合自己公司业务需求的 API,但是他们可能使用的都是同一套开发框架在 Python 中,有多种接口开发框架可供选择:
| 维度 | FastAPI | Django |
|---|---|---|
| 发布时间 | 2018 年 | 2005 年 |
| 开发语言 | Python | Python |
| 架构风格 | 微框架,基于 Starlette 和 Pydantic | 大型框架,支持全栈开发,内置 ORM、Admin 等 |
| 性能 | 高,基于 ASGI,支持异步特性 | 中等,依赖于 WSGI |
| 异步支持 | 原生支持,使用 async/await | 通过 Django 3.0+ 开始支持异步(仍在逐步完善) |
| 数据验证 | 内置 Pydantic,支持强类型和自动验证 | 依赖于表单类(如 Forms 和 DRF 的 serializers) |
| 自动文档生成 | 内置支持 OpenAPI 和 Swagger UI | 不原生支持,需依赖 Django REST Framework (DRF) |
| 学习曲线 | 中等,需了解类型注解和异步编程 | 较高,包含多种内置组件,适合系统性学习 |
| 社区和生态 | 新兴框架,社区成长迅速,但生态略小 | 最悠久且成熟,生态完善,适合大型项目 |
| 可扩展性 | 高,强大的原生功能减少了扩展需求 | 中等,内置组件强大,但灵活性相对较低 |
| 依赖注入支持 | 原生支持依赖注入 | 无原生支持 |
| 生产就绪 | 稳定性较高,但相比 Flask 和 Django 使用年限较短 | 成熟稳定,被广泛应用于大型生产系统 |
| 调试工具 | 支持 Starlette 的调试功能 | 内置调试模式,适合开发测试 |
| 使用场景 | 高性能、异步处理或自动文档的场景 | 大型系统开发,支持复杂业务逻辑和多用户系统 |
| ORM 支持 | 无原生支持,需借助 Tortoise-ORM 等 | 原生支持,提供 Django ORM |
| Admin 界面 | 无,需要自行开发 | 原生支持,提供强大的 Admin 管理后台 |
| 支持的 Python 版本 | Python 3.6+ | Python 3.8+ |
| 对类型注解的支持 | 原生支持,强类型友好 | 不直接支持,但可结合第三方库使用 |
FastAPI
FastAPI的作者 Tiangolo(真名 Sebastián Ramírez)是一位来自哥伦比亚、目前居住在德国柏林的著名开源软件工程师。在 7 岁时就从天主教学校退学了。当时的原因非常有趣:因为他总是问太多的问题,被老师认为“太难对付”。
在退学之后,他的父母并没有强迫他回到传统学校,而是支持他居家自学。涵盖了算法、机器学习、密码学和人工智能等核心计算机科学领域。甚至连英语也是通过在线资源自学的。
他在 23 岁左右曾感到有些迷茫,因为当时他虽然已经掌握了大量深奥的知识,但由于 没有正式学历和文凭,他在进入职场初期感到有些不自信。然而,他最终凭借在 GitHub 上的开源贡献和扎实的技术实力证明了自己,先后在迪拜和德国柏林的知名科技公司(如 Explosion AI 和 Forethought)担任高级/首席工程师。
目前的核心工作是全职开源维护者。
安装
-
pip install fastapi:只安装核心框架。 -
pip install "fastapi[standard]":安装核心框架的同时,自动捆绑安装开发一个标准项目所需的常用工具,包括:Uvicorn、FastAPI CLI、HTTPX、Pydantic
方括号语法是 “可选依赖组”(Optional Dependencies / Extras)。你可以把它理解为增强版安装。如果你已经输入了pip install fastapi,依然可以通过pip install "fastapi[standard]"补全配件。
相比之下,opencv采取的是发布多个独立包的策略:
opencv-pythonopencv-python-headlessopencv-contrib-pythonopencv-contrib-python-headless
opencv的几个包是互斥的。装了 A 就不能装 B。一旦安装了多个,必须全部卸载,再单独安装你需要的版本。
ASGI
FastAPI 是一个现代的、快速的(高性能)、功能强大的 Web 框架,用于构建 API。运行在 ASGI 服务器上。
- A (Asynchronous) :异步。支持
async/await特性,允许程序在等待 IO(如读数据库)时去处理其他请求。 - S (Server) :服务器。
- G (Gateway) :网关。
- I (Interface) :接口/标准。
ASGI 服务器技术选型
Uvicorn:目前最流行的轻量级、超快速的 ASGI 服务器。
单独安装方式:
pip install uvicorn
Daphne:最早的 ASGI 服务器(由 Django 团队开发)。
Hypercorn:支持 HTTP/2 和 HTTP/3 的 ASGI 服务器。
接口文档
FastAPI 最受开发者欢迎的特性之一就是它原生内置了两套完全自动生成的交互式文档。你不需要写一行额外的文档代码,只要你写好了 API 逻辑,文档就实时生成了。
这两套文档分别是 Swagger UI 和 ReDoc。
FastAPI 并不是硬生生写了两套网页。它的逻辑是:
- 解析代码 :FastAPI 扫描你的路径、参数、Pydantic 模型。
- 生成 OpenAPI 规范 :它会自动生成一个符合国际标准 (OpenAPI/Swagger) 的 JSON 架构文件 (你可以访问
http://127.0.0.1:8000/openapi.json看到它)。 - 渲染 UI :Swagger UI 和 ReDoc 只是两个“皮肤”,它们读取这个 JSON 文件并将其可视化。
Swagger UI
访问地址:http://127.0.0.1:8000/docs
核心用途:调试与测试。
直接测试 (Try it out):你不需要安装 Postman 或使用 curl 命令。点击 API 展开,点击 "Try it out",填入参数,直接就能看到服务器返回的结果。
实时校验:如果你定义的 Pydantic 模型要求某个字段是 int,你在 Swagger 里输入字符串,它会立刻变红提示你错误。
ReDoc
访问地址:http://127.0.0.1:8000/redoc
核心用途:阅读与归档。
极其整洁:它采用经典的三栏式布局(导航、说明、代码示例),非常适合给前端同学或第三方合作伙伴阅读。
基础用法
from fastapi import FastAPI, Header, HTTPException, Query, Path
from pydantic import BaseModel
from typing import Optional, Dict
app = FastAPI()
# Pydantic Models
class PostExampleModel(BaseModel):
title: Optional[str] = "默认标题"
content: Optional[str] = "默认内容"
class PutExampleModel(BaseModel):
name: Optional[str] = "默认名称"
description: Optional[str] = "默认描述"
class ApiResourcePostModel(BaseModel):
key: str
class ApiResourcePutModel(BaseModel):
field: str
# GET /get_example/{user_id}
@app.get("/get_example/{user_id}")
def get_example(
user_id: int,
name: Optional[str] = Query("默认名字"),
user_agent: Optional[str] = Header("未知")
):
"""
**方法:** GET
**示例请求:**
http:day3.ipynb
GET /get_example/123?name=Alice HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
**示例响应:**
json
{
"user_id": 123,
"name": "Alice",
"user_agent": "Mozilla/5.0"
}
"""
return {"user_id": user_id, "name": name, "user_agent": user_agent}
# POST /post_example
@app.post("/post_example")
def post_example(
data: PostExampleModel,
authorization: Optional[str] = Header("无授权")
):
"""
**方法:** POST
**示例请求:**
http
POST /post_example HTTP/1.1
Host: example.com
Content-Type: application/json
Authorization: Bearer token
{
"title": "示例标题",
"content": "示例内容"
}
**示例响应:**
json
{
"title": "示例标题",
"content": "示例内容",
"authorization": "Bearer token"
}
"""
return {"title": data.title, "content": data.content, "authorization": authorization}
# PUT /put_example/{item_id}
@app.put("/put_example/{item_id}")
def put_example(
item_id: int,
data: PutExampleModel,
x_custom_header: Optional[str] = Header("无自定义头")
):
"""
**方法:** PUT
**示例请求:**
http
PUT /put_example/456 HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
X-Custom-Header: 自定义值
name=新名称&description=新描述
**示例响应:**
json
{
"item_id": 456,
"name": "新名称",
"description": "新描述",
"custom_header": "自定义值"
}
"""
return {
"item_id": item_id,
"name": data.name,
"description": data.description,
"custom_header": x_custom_header,
}
# DELETE /delete_example
@app.delete("/delete_example")
def delete_example(
confirm: bool = Query(False),
authorization: Optional[str] = Header("无授权")
):
"""
**方法:** DELETE
**示例请求(确认删除):**
http
DELETE /delete_example?confirm=true HTTP/1.1
Host: example.com
Authorization: Bearer token
**示例响应(确认删除):**
json
{
"deleted": true,
"authorization": "Bearer token"
}
**示例请求(未确认删除):**
http
DELETE /delete_example HTTP/1.1
Host: example.com
**示例响应(未确认删除):**
json
{
"message": "删除未确认"
}
HTTP 状态码:400
"""
if not confirm:
raise HTTPException(status_code=400, detail="删除未确认")
return {"deleted": confirm, "authorization": authorization}
# GET /api/resource/{resource_id}
@app.get("/api/resource/{resource_id}")
def api_resource_get(
resource_id: str,
detail: Optional[str] = Query("basic")
):
"""
**方法:** GET
**请求示例:**
GET /api/resource/abc123?detail=full HTTP/1.1
Host: example.com
**响应示例:**
json
{
"method": "GET",
"resource_id": "abc123",
"detail": "full"
}
"""
return {"method": "GET", "resource_id": resource_id, "detail": detail}
# POST /api/resource/{resource_id}
@app.post("/api/resource/{resource_id}")
def api_resource_post(
resource_id: str,
data: ApiResourcePostModel
):
"""
**方法:** POST
**请求示例:**
POST /api/resource/abc123 HTTP/1.1
Host: example.com
Content-Type: application/json
{
"key": "value"
}
**响应示例:**
json
{
"method": "POST",
"resource_id": "abc123",
"data": {
"key": "value"
}
}
"""
return {"method": "POST", "resource_id": resource_id, "data": data.dict()}
# PUT /api/resource/{resource_id}
@app.put("/api/resource/{resource_id}")
def api_resource_put(
resource_id: str,
data: ApiResourcePutModel
):
"""
**方法:** PUT
**请求示例:**
PUT /api/resource/abc123 HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
field=new%20value
**响应示例:**
json
{
"method": "PUT",
"resource_id": "abc123",
"data": {
"field": "new value"
}
}
"""
return {"method": "PUT", "resource_id": resource_id, "data": data.dict()}
# DELETE /api/resource/{resource_id}
@app.delete("/api/resource/{resource_id}")
def api_resource_delete(
resource_id: str,
confirm: bool = Query(False)
):
"""
**方法:** DELETE
**请求示例:**
DELETE /api/resource/abc123?confirm=true HTTP/1.1
Host: example.com
**响应示例:**
json
{
"method": "DELETE",
"resource_id": "abc123",
"confirm": true
}
"""
return {"method": "DELETE", "resource_id": resource_id, "confirm": confirm}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
下面是异步函数作为 FastAPI 的返回值的示例
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import asyncio
import random
app = FastAPI()
async def async_range(n):
for i in range(n):
yield f"<p>{i}</p>"
await asyncio.sleep(random.randint(1, 3) * 1)
@app.get("/stream")
async def stream_numbers():
return StreamingResponse(async_range(5), media_type="html", background=None)
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
如何验证 FastAPI 的真实并发能力?
通过代码而不是浏览器。如果你在 同一个浏览器 (比如 Chrome)中打开两个标签页,同时请求同一个 URL:
- 浏览器的锁定机制 :大多数现代浏览器为了防止过度消耗服务器资源,会对完全相同的 URL 进行连接锁定。它会等待第一个请求完成后,才发出第二个。
数据流
在 Web 接口请求中,数据主要分布
| 数据来源 (Location) | 浏览器/HTTP 格式 | FastAPI 参数定义方式 | Python 转换后的类型 | 典型用途 |
|---|---|---|---|---|
| Path (路径) | /users/10 | user_id: int | int | 定位特定资源 |
| Query (查询) | ?q=fast&page=1 | q: str, page: int | str,int | 搜索、排序、分页 |
| Body (JSON) | {"id": 1, "name": "AI"} | item: Item(BaseModel) | Pydantic Model |