API 代表应用程序编程接口,是软件开发中最基本的概念之一。它允许程序通过发送和接收数据与其他服务进行交互。在 API 网络通信中,最常用的标准之一是REST,它依赖于JSON格式或键值对,类似于 Python 的字典。
在本文中,我想专注于实际实现,并立即开始构建 API。如果你想在 Python 中构建一个 API,你可以选择几种框架。Flask-RESTful、Django Rest Framework 和 FastAPI 是最流行的。然而,FastAPI 是专门为快速 API 应用设计的。为什么我推荐你首选 FastAPI?
- 自动生成文档 — FastAPI 根据 OpenAPI 标准自动生成全面的文档,节省时间和精力。
- 快速简单 — 设计用于高性能和高效执行。
- 数据验证 — FastAPI 构建在 Pydantic 之上,提供一系列有用的功能,如数据验证和异步支持。
虽然 Django Rest Framework 在许多公司中也常用,但让我们从 FastAPI 开始,因为它的好处更多。话虽如此,让我们在简要理论介绍之后,继续迈向编写 FastAPI 代码的第一步。
简要介绍
请求的Endpoint
如前所述,API 作为不同服务和应用之间通信的手段。这种通信通常由服务器端基础设施和 API 的后端实现来促成。这个后端的目的可能是在不需要直接连接到数据库的情况下提供对数据的访问,从而最大程度地减少对数据库的请求。
在 API 通信中,我们通常有一个指定的终点,它可能出现在网站末尾的 /api/item/5。这种方法非常有利,因为它允许我们利用同一终点从各种设备请求数据,并接收相同的响应。
HTTP 方法
虽然我不想深入讨论理论概念,但理解在处理 API 时不同方法之间的区别是很重要的。让我们快速回顾一下最常用的方法:
- GET — 用于检索数据
- POST — 用于写入新数据
- DELETE — 删除你的数据
- PUT — 更新现有信息
虽然还有其他选项,但这些是你应该开始使用的基本方法。重要的是要注意 POST 和 PUT 之间的区别。两者都可以用于添加数据,但 POST 用于写入新项目,而 PUT 用于使用更新后的值更新现有项目。
请记住,这只是编写 API 的指南。你可以按照自己的方式使用这些方法,但最好遵循行业标准。今天我想专注于实现一个简单的 GET 方法。
构建你的第一个 API
运行第一个 FastAPI 服务器
首先,我们需要安装所有依赖项。你至少需要安装 fastapi
包以及 uvicorn
服务器和 pydantic
。第一行应安装所有列出的库,但请检查是否已安装了所有包。
pip install fastapi[all]
pip install uvicorn
pip install pydantic
现在,让我们通过使用装饰器和必要的方法和终点来创建一个基本的 FastAPI 应用程序。在这个例子中,我们将使用 GET 方法检索使用空终点 /
的数据。在本文中,我不会讨论装饰器,这超出了 API 主题的范围。然而,在 FastAPI 上下文中,我们利用 应用程序名称、方法 和 终点。在函数之前,你可能会注意到一行代码,它负责将所有 FastAPI 进程添加到我们的方法中。
from fastapi import FastAPI
app = FastAPI()
@app.get('/') # get method to empty endpoint
def first_response():
return {"response": "first"}
要查看响应,你可以使用 uvicorn
运行服务器。默认情况下,你的服务器位于端口 8000,并且可以通过 http://127.0.0.1:8000 访问。在开发过程中,你可以使用 --reload
选项确保服务器对代码的任何更改做出反应。
uvicorn main:app
uvicorn main:app --reload
作为另一种选择,你也可以使用 curl
来获取相同的响应。
curl 127.0.0.1:8000
#{"response": "first"}
curl 127.0.0.1:8000 -X POST
#{"detail":"Method Not Allowed"}
最后,我们可以使用 requests
库来访问我们的终点,并在 Python 中打印响应。
import requests
print(requests.get('http://127.0.0.1:8000').json())
#{'response': 'first'}
使用 JSON 文件中的数据
让我们继续下一步,处理一些实际数据。为了简化目前的情况,我们可以创建一个带有一些输入的 JSON 文件。例如,我们可以从纳斯达克(NASDAQ)选择三只股票,并创建一个带有可以用于获取所需项目并执行其他方法的符号的示例。
{
"stocks": [
{
"symbol": "TSLA",
"stockname": "特斯
拉公司普通股",
"lastsale": "$235.45",
"country": "美国",
"ipoyear": 2010
},
{
"symbol": "NVDA",
"stockname": "英伟达公司普通股",
"lastsale": "$477.76",
"country": "美国",
"ipoyear": 1999
},
{
"symbol": "AMZN",
"stockname": "亚马逊公司普通股",
"lastsale": "$146.74",
"country": "美国",
"ipoyear": 1997
}
]
}
现在我们可以修改我们的代码,不再检索所有项目,而是只检索包含我们想要的特定符号的项目。换句话说,在后端执行过滤操作。为了实现这一点,我们执行以下额外的操作:
- 确定
pydantic
的模型。 - 读取 JSON 文件并将其内容存储在字典中。
- 使用股票符号对字典中的数据进行过滤,仅检索所需的项目。如果找不到符号,则引发 404 状态码(未找到)的异常。
看起来很简单,但这是每个 FastAPI 应用程序的基本部分 —— 模式、数据和方法。
from fastapi import FastAPI, HTTPException, Query
from pydantic import BaseModel
from typing import Optional
import json
app = FastAPI()
class Stock(BaseModel):
symbol: str
stockname: str
lastsale: str
country: str
ipoyear: Optional[int] = None
with open('stocks.json', 'r') as f:
stocks = json.load(f)['stocks']
@app.get('/stock/{stock_symbol}', status_code=200)
def get_stock(stock_symbol: str) -> Stock:
stock = [stock for stock in stocks if stock['symbol'] == stock_symbol]
if len(stock) == 0:
raise HTTPException(
status_code=404, detail=f"No stock {stock_symbol} found."
)
return stock[0]
现在,我们需要更改请求的终点以获取亚马逊股票数据。
import requests
print(requests.get('http://127.0.0.1:8000/stock/AMZN').json())
#{'symbol': 'AMZN', 'stockname': 'Amazon.com Inc. Common Stock', 'lastsale': '$146.74', 'country': 'United States', 'ipoyear': 1997}
如前所述,我们的文档现在应该是可用的。你可以在浏览器地址栏中键入 http://127.0.0.1:8000/docs 来访问它。当你继续添加更多方法时,你可以轻松导航到相同位置以找到它们。对于那些刚开始使用你的 API 的人来说,这个文档将特别有帮助。
在我们的代码中识别了 Pydantic 模型后,我们现在可以确定输出响应的模式。这也是更好地理解各种 API 方法以及我们可以从中获取什么的一个很好的方法。
将 PostgreSQL 数据库连接到你的 API
过去,我们只使用本地文件。然而,在大多数情况下,你将需要在后端使用数据库。为了实现这一点,我们将连接 PostgreSQL 数据库,并尝试在我们的 GET 方法中使用它。在数据库端 —— 它是一个简单的 SELECT 语句。但是,我们需要为 FastAPI 的使用正确地识别一切。
该过程涉及 SQLAlchemy 库,它是 Python 中最流行的对象关系映射(ORM)操作之一。
为了将凭据存储在单独的文件中,创建一个 config.py
,其中包含以下代码。这个文件没有包含在我的 GitHub 示例中,因为它包含敏感数据,但你可以使用以下示例。
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
sqlalchemy_string: str = "postgresql://user:passwordp@host/db"
settings = Settings()
让我们通过结构化这一部分到 database.py
文件中,创建引擎并准备 FastAPI 会话的数据库。在这里,我们使用来自 config.py
文件的设置。
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from config import settings
engine = create_engine(
settings.sqlalchemy_string, connect_args={'sslmode':'require'}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
接下来,我们需要将模型链接到数据库中的相关表。我们专门处理包含纳斯达克股票数据的表,并为此使用一个简单的模型。为了我们的示例目的,我们将坚持基本模型。
from sqlalchemy import Column, Integer, String, Float, BigInteger
from database import Base
class Stock(Base):
__tablename__ = "nasdaq_stocks"
symbol = Column(String, primary_key=True)
stockname = Column(String)
lastsale = Column(String)
netchange = Column(Float)
percentchange = Column(String)
marketcap = Column(BigInteger)
country = Column(String, nullable=True)
ipoyear = Column(Integer, nullable=True)
volume = Column(Integer)
sector = Column(String, nullable=True)
industry = Column(String, nullable=True)
我更喜欢将我们的 pydantic
模型存储在单独的文件中,以避免混淆。让我们将文件命名为 schemas.py
,并在其中添加相关模型。重要的是正确配置 orm_mode
,因为我们使用的是 SQLAlchemy 和数据库。
from pydantic import BaseModel
from typing import Optional
class StockBase(BaseModel):
symbol: str
stockname: str
lastsale: str
country: str
ipoyear: Optional[int] = None
volume: int
class StockCreate(StockBase):
pass
class Stock(StockBase):
class Config:
orm_mode = True
我们必须在数据库端指定对数据库执行 CRUD(创建、读取、更新、删除)操作的代码。稍后在 main
脚本中,只使用函数将更加方便。对于我们的基本 GET 方法,通过符号执行简单的过滤查询就足够了。这是一个最基本的 crud.py
文件的示例。
from sqlalchemy.orm import Session
import models, schemas
def get_stock(db: Session, symbol: str):
return db.query(models.Stock).filter(models.Stock.symbol == symbol).first()
我们已经完成了实现 API 所需的所有准备工作。由于我们正在处理数据库,因此需要在脚本中包含一些额外的细节。get_db
函数负责与数据库建立连接,我们已将其包含在 Depends
FastAPI 类中。这是工作代码的最终示例。
from fastapi import FastAPI, HTTPException, Query, Depends
from sqlalchemy.orm import Session
import crud, models, schemas
from database import SessionLocal, engine
models.Base.metadata.create_all(bind=engine)
app = FastAPI(
title="NASDAQ 股票",
description="开始在开发中使用 FastAPI",
version="0.1"
)
# 依赖项
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get('/stock/{symbol}', response_model=schemas.Stock, status_code=200)
def get_stock(symbol: str, db: Session = Depends(get_db)) -> models.Stock:
db_stock = crud.get_stock(db, symbol=symbol)
if db_stock is None:
raise HTTPException( status_code=404, detail=f"No stock {symbol} found." )
return db_stock
没有发生重大变化,但现在我们可以搜索整个数据库,而不仅仅是 JSON 文件中的三只股票。修改输出也很简单,因为我们可以在我们的 pydantic
模型中添加或删除字段。我们有一个额外的字段叫做 volume
,我添加它是为了观察差异。
import requests
print(requests.get('http://127.0.0.1:8000/stock/AAL').json())
#{'symbol': 'AAL', 'stockname': '美国航空集团股份有限公司普通股', 'lastsale': '$12.31', 'country': '美国', 'ipoyear': None, 'volume': 14803753}
ORM 与市场上提供的各种数据库选项无缝集成,实现高效的集成而无需进行任何修改。你可以参考有关在 FastAPI 中使用 SQL 数据库的全面文档。
总结
在本文中,我们提供了 FastAPI 的概述,以及它简化了 REST API 实现的能力。与其他有用的 Python 依赖项一起,FastAPI 提供了许多重要功能:
- 用于数据验证的 Pydantic。
- 用于与数据库进行 ORM 通信的 SQLAlchemy。
FastAPI 不仅限于将数据作为响应返回只使用 GET 方法。它提供了 REST API 的全部功能,还有其他有价值的方法,例如 POST、PUT 和 DELETE。在下一篇文章中,我们将更深入地探讨这些选项,并通过构建一个基本但可以投入生产的 API 来总结。