import random

導(dǎo)入(FastAPI)


FastAPI 的導(dǎo)入聲明則被歸類到不同的包中,所以看起來很復(fù)雜。下面的代碼塊導(dǎo)入了對表單字段輸入的支持、對返回不同類型返回的支持,以及通過模板引擎渲染靜態(tài)文件和 HTML 文件的支持。

from fastapi import FastAPI, Form, Request
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
import random
import uvicorn

最最基礎(chǔ)的導(dǎo)入應(yīng)該長這個樣子:

from fastapi import FastAPI
from pydantic import BaseModel
import random # needed for generating a random number for an API
import uvicorn # optional if you run it directly from terminal

初始化(Flask)


Flask 可以處理靜態(tài)文件及模板引擎,初始化代碼如下:

app = Flask(__name__)

初始化(FastAPI)


除了標(biāo)準(zhǔn)初始化流程,還需“掛載”靜態(tài)文件路徑。同樣,模板引擎渲染也需要聲明一個變量。大部分的初始化代碼都是在用基于 Pydantic 的語法創(chuàng)建數(shù)據(jù)模型類。

app = FastAPI()
# 可選,用于渲染靜態(tài)文件
app.mount("/static", StaticFiles(directory="static"), name="static")
# 可選,用于模板引擎渲染網(wǎng)頁
templates = Jinja2Templates(directory="templates")
# 視使用情況
class Item(BaseModel):
language = 'english'

Hello World(Flask)


創(chuàng)建一個可以返回字符串的路由。

@app.route('/')
def hello():
return "Hello World!"

Hello World(FastAPI)

FastAPI 版本的“Hello World”如下。因為默認(rèn)返回類型為 JSON,所以需要修改 response_class 到 PlainTextResponse 來返回字符串。async 字段會讓異步代碼更簡單,雖然不是必需,但除非你的代碼不支持異步,否則我建議你最好加上。

@app.get("/", response_class=PlainTextResponse)
async def hello():
return "Hello World!"

隨機(jī)數(shù)(Flask)


在 Flask 服務(wù)器上返回隨機(jī)生成數(shù)字 API 的代碼如下。

@app.route('/random-number')
def random_number():
return str(random.randrange(100))

隨機(jī)數(shù)(FastAPI)


FastAPI 的代碼只需簡單修改:

@app.get('/random-number', response_class=PlainTextResponse)
async def random_number():
return str(random.randrange(100))

檢查 isAlpha(Flask)


下面我們將測試一個接收名為 text 的查詢參數(shù),并返回 JSON 結(jié)果的 API。在 Flask 中,這一步是通過路由裝飾器設(shè)置完成的。在這里我們將其設(shè)置為僅接受 GET 請求。

@app.route('/alpha', methods=['GET'])
def alpha():
text = request.args.get('text', '')
result = {'text': text, 'is_alpha' : text.isalpha()}
return jsonify(result)

檢查 isAlpha(FastAPI)

首先定義 HTTP 請求方法和裝飾器,這在 FastAPI 中被稱作操作(operation)。GET 操作需要調(diào)用 app.get 來完成;而對于多個功能相同的 HTTP 請求方法則需要將其邏輯打包到一個函數(shù)中,然后在其各自的操作中獨(dú)立調(diào)用。查詢參數(shù)需要和類型提示一起指定。text: str 表示一個需要字符串查詢參數(shù) text。也可以通過指定默認(rèn)值 text = ‘text’,使其成為一個可選參數(shù)。

app.get('/alpha')
async def alpha(text: str):
result = {'text': text, 'is_alpha' : text.isalpha()}
return result

創(chuàng)建新 User(Flask)


添加新數(shù)據(jù)到數(shù)據(jù)庫,通常需要使用 POST 請求。下面的例子接收兩個表單字段,返回一個 JSON 結(jié)果。

@app.route('/create-user', methods=['POST'])
def create_user():
id = request.form.get('id', '0001')
name = request.form.get('name', 'Anonymous')
# 用于認(rèn)證、校驗、更新數(shù)據(jù)庫
data = {'id': id, 'name': name}
result = {'status_code': '0', 'status_message' : 'Success', 'data': data}
return jsonify(result)

創(chuàng)建新 User(FastAPI)


POST 請求由 app.post 裝飾器處理。默認(rèn)情況下的實現(xiàn)是基于 JSON 或查詢參數(shù)的,如果要聲明輸入?yún)?shù),則需指定 Form(…)。

@app.post('/create-user')
async def create_user(id: str = Form(...), name: str = Form(...)):
# 用于認(rèn)證、校驗、更新數(shù)據(jù)庫
data = {'id': id, 'name': name}
result = {'status_code': '0', 'status_message' : 'Success', 'data': data}
return result

更新 Language(Flask)


目前為止,本文已經(jīng)介紹了查詢參數(shù)以及表單字段的代碼。下面這個例子則將根據(jù) JSON 輸入更新一個名為 language 的變量,這類對現(xiàn)有數(shù)據(jù)更新的操作,建議使用 PUT 方法。展示的代碼中沒有直接指定 PUT 方法,而是通過條件語句來實現(xiàn)。

@app.route('/update-language', methods=['POST', 'PUT', 'GET', 'DELETE'])
def update_language():
language = 'english'
if request.method == 'PUT':
json_data = request.get_json()
language = json_data['language']
return "Successfully updated language to %s" % (language)

更新 Language(FastAPI)


同理,PUT 操作是由 app.put 裝飾器處理。在初始化的過程中,我們曾定義過以下的 class:

class Item(BaseModel):
language = 'english'

然后我們需要將這個 class 作為 item 輸入?yún)?shù)的類型提示。直接使用 variable_name.attribute_name 語法調(diào)用即可,解析會在后臺正確完成。在本例中,我們用 item.language。

@app.put('/update-language', response_class=PlainTextResponse)
async def update_language(item: Item):
language = item.language
return "Successfully updated language to %s" % (language)

HTML 網(wǎng)頁(Flask)

在 Flask 中服務(wù)網(wǎng)頁相對比較簡單,使用 Jinja2 模板引擎即可完成。我們只需要在 templates 的文件夾中聲明 HTML 文件,如果你需要提供靜態(tài)文件,則需要將其放入名為 static 的文件夾中。下面的代碼示例使用 index.html 渲染網(wǎng)頁,變量可以作為輸入?yún)?shù)傳入:

@app.route('/get-webpage', methods=['GET'])
def get_webpage():
return render_template('index.html', message="Contact Us")

HTML 網(wǎng)頁(FastAPI)


在初始化過程中,我們“掛載”了一個 static 文件夾,用于提供靜態(tài)文件:

app.mount("/static", StaticFiles(directory="static"), name="static")

基于 Jinja2Templates 創(chuàng)建了一個變量:

templates = Jinja2Templates(directory="templates")

這些都是用于渲染 HTML 模板的。為提供 HTML 網(wǎng)頁,我們需要將 response_class 改為 HTMLResponse。如果你用的不是模板引擎,那么可以直接將結(jié)果返回為字符串。
Request 參數(shù)需要返回模板以及自定義參數(shù)。

@app.get('/get-webpage', response_class=HTMLResponse)
async def get_webpage(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "message": "Contact Us"})

文件響應(yīng)(Flask)

若要將文件返回給用戶,最佳的處理方式是通過內(nèi)置函數(shù) send_from_directory,如果路徑或文件是通過用戶輸入獲得,那么則更應(yīng)如此。該內(nèi)置函數(shù)接受兩個主要輸入:

文件路徑

文件名

另外,你也可以額外聲明其他參數(shù),諸如:as_attachment,通過修改 Content-Disposition 頭來指定其為附件。路徑參數(shù)可以通過<type:variable_name>語法來指定。在本例中,我們用<string:language>

@app.route('/get-language-file/<string:language>', methods=['GET'])
def get_language_file(language):
return send_from_directory('./static/language', language + '.json', as_attachment=True)

文件響應(yīng)(FastAPI)

FastAPI 根據(jù)要求和需要,提供了相當(dāng)多的響應(yīng)類型。如果需要返回文件,可以用 FileResponse 或 StreamingResponse。在本文中我們將展示 FileResponse 的使用案例,它接受以下輸入:

path:需要流式傳輸?shù)奈募窂?/p>

headers:任何自定義頭,以字典形式輸入

media_type:給定媒體類型的字符串。默認(rèn)通過文件名或路徑推斷媒體類型。

filename:設(shè)置后,會被響應(yīng)的 Content-Disposition 引用。代碼展示:

@app.get('/get-language-file/{language}')
async def get_language_file(language: str):
file_name = "%s.json" % (language)
file_path = "./static/language/" + file_name
return FileResponse(path=file_path, headers={"Content-Disposition": "attachment; filename=" + file_name})

主函數(shù)(Flask)


Flask 中主函數(shù)應(yīng)如下:

if __name__ == '__main__':
    app.run('0.0.0.0',port=8000)

然后在終端中通過這條命令運(yùn)行文件:

python myapp.py

主函數(shù)(FastAPI)


FastAPI 則需要導(dǎo)入 uvicorn。

import uvicorn

并且用如下方法指定主函數(shù)(myapp 為文件名,app 為 FastAPI 實例所聲明的變量名):

if __name__ == '__main__':
uvicorn.run('myapp:app', host='0.0.0.0', port=8000)

然后在終端中正常運(yùn)行即可:

python myapp.py

更好的方法則是不調(diào)用主函數(shù),在終端中通過 uvicorn 直接運(yùn)行。

uvicorn myapp:app

還可以再額外指定一些參數(shù),諸如:

reload:啟用自動加載功能,修改文件后會刷新服務(wù)器。對本地開發(fā)非常有用。

port:服務(wù)器端口,默認(rèn)為 8000。這條代碼可以將端口號改為 5000:

uvicorn myapp:app --reload --port 5000

Flask 服務(wù)器


以下為 Flask 服務(wù)器中的完整 代碼。

# 導(dǎo)入聲明
from flask import Flask, request, jsonify, render_template, send_from_directory
import random
# 初始化
app = Flask(__name__)

# hello world,GET 方法,返回字符串
@app.route('/')
def hello():
return "Hello World!"
# 隨機(jī)數(shù),GET 方法,返回字符串
@app.route('/random-number')
def random_number():
return str(random.randrange(100))

# 檢查 isAlpha,GET 方法,查詢參數(shù),返回 JSON
@app.route('/alpha', methods=['GET'])
def alpha():
text = request.args.get('text', '')
result = {'text': text, 'is_alpha' : text.isalpha()}
return jsonify(result)

# 創(chuàng)建新 user,POST 方法,表單字段,返回 JSON
@app.route('/create-user', methods=['POST'])
def create_user():
id = request.form.get('id', '0001')
name = request.form.get('name', 'Anonymous')

# 用于認(rèn)證、校驗、更新數(shù)據(jù)庫
data = {'id': id, 'name': name}
result = {'status_code': '0', 'status_message' : 'Success', 'data': data}
return jsonify(result)

# 更新 language,PUT 方法,JSON 輸入,返回字符串
@app.route('/update-language', methods=['POST', 'PUT', 'GET', 'DELETE'])
def update_language():
language = 'english'

if request.method == 'PUT':
json_data = request.get_json()
language = json_data['language']

return "Successfully updated language to %s" % (language)

# 服務(wù)網(wǎng)頁,GET 方法,返回 HTML
@app.route('/get-webpage', methods=['GET'])
def get_webpage():
return render_template('index.html', message="Contact Us")

# 文件響應(yīng),GET 方法,返回文件為附件
@app.route('/get-language-file/<string:language>', methods=['GET'])
def get_language_file(language):
return send_from_directory('./static/language', language + '.json', as_attachment=True)

# main
if __name__ == '__main__':
app.run('0.0.0.0',port=8000)

FastAPI 服務(wù)器


以下 代碼 是使用 FastAPI 實現(xiàn)的相同功能。

# 導(dǎo)入聲明
from fastapi import FastAPI, Form, Request
from fastapi.responses import PlainTextResponse, HTMLResponse, FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

from pydantic import BaseModel
import random
import uvicorn

# 初始化
app = FastAPI()

# “掛載”靜態(tài)文件夾,用于渲染靜態(tài)文件
app.mount("/static", StaticFiles(directory="static"), name="static")

# Jinja2 模板,用于利用模板引擎返回網(wǎng)頁
templates = Jinja2Templates(directory="templates")

# Pydantic 數(shù)據(jù)模型 class
class Item(BaseModel):
#language: str
language = 'english'

# hello world,GET 方法,返回字符串
@app.get("/", response_class=PlainTextResponse)
async def hello():
return "Hello World!"

# 隨機(jī)數(shù),GET 方法,返回字符串
@app.get('/random-number', response_class=PlainTextResponse)
async def random_number():
return str(random.randrange(100))

# 檢查 isAlpha,GET 方法,查詢參數(shù),返回 JSON
@app.get('/alpha')
async def alpha(text: str):
result = {'text': text, 'is_alpha' : text.isalpha()}
return result

# 創(chuàng)建新 user,POST 方法,表單字段,返回 JSON
@app.post('/create-user')
async def create_user(id: str = Form(...), name: str = Form(...)):
# 用于認(rèn)證、校驗、更新數(shù)據(jù)庫
data = {'id': id, 'name': name}
result = {'status_code': '0', 'status_message' : 'Success', 'data': data}
return result
# 更新 language,PUT 方法,JSON 輸入,返回字符串
@app.put('/update-language', response_class=PlainTextResponse)
async def update_language(item: Item):
language = item.language
return "Successfully updated language to %s" % (language)

# 服務(wù)網(wǎng)頁,GET 方法,返回 HTML
@app.get('/get-webpage', response_class=HTMLResponse)
async def get_webpage(request: Request):
return templates.TemplateResponse("index.html", {"request": request, "message": "Contact Us"})

# 文件響應(yīng),GET 方法,返回文件為附件
@app.get('/get-language-file/{language}')
async def get_language_file(language: str):
file_name = "%s.json" % (language)
file_path = "./static/language/" + file_name

return FileResponse(path=file_path, headers={"Content-Disposition": "attachment; filename=" + file_name})

# main
if __name__ == '__main__':
uvicorn.run('myapp:app', host='0.0.0.0', port=8000)

第三步:文檔

在成功運(yùn)行 FastAPI 服務(wù)器后,你將得到兩個用于文檔的額外路由。交互式文檔(Swagger UI)第一個路由是交互式文檔 Swagger UI。如果在端口 8000 上運(yùn)行的服務(wù)器,那么就可以通過以下 URL 訪問:

http://localhost:8000/docs

進(jìn)入之后你會看到以下界面:

這是一個交互式的文檔,你可以在其中單獨(dú)測試 API。點擊 /alpha 路由時,你應(yīng)該能看到以下界面:

在 text 字段輸入字符串后,點擊“Try it out”按鈕。接著再點擊“Execute”按鈕,會得到以下結(jié)果:

ReDoc

除此之外,FastAPI 還提供另一種文檔 ReDoc。通過以下 URL 訪問:

http://localhost:8000/redoc

你會看到以下文檔界面。

第四步:結(jié)論

總結(jié)時間:

本文開篇先是 FastAPI 核心概念的背景介紹,然后是 Flask 和 FastAPI 運(yùn)行所需模塊的安裝。安裝結(jié)束后,我們測試了不同 HTTP 請求方法、輸入請求、輸出響應(yīng)下的幾種 API,并分別對比了這些功能在 Flask 和 FastAPI 下代碼的區(qū)別。

本文概括介紹了如何將 Flask 服務(wù)器遷移到 FastAPI 服務(wù)器的基本過程,并列舉了使用實例。 延伸閱讀

https://medium.com/better-programming/migrate-from-flask-to-fastapi-smoothly-cc4c6c255397](https://medium.com/better-programming/migrate-from-flask-to-fastapi-smoothly-cc4c6c255397 參考資料

Uvicorn 的 Github 頁面:

https://github.com/encode/uvicorn

Uvicorn 文檔:

https://www.uvicorn.org/

FastAPI 的 Github 頁面:

https://github.com/tiangolo/fastapi

FastAPI 文檔:

https://fastapi.tiangolo.com/

官方文檔翻譯:

https://github.com/apachecn/fastapi-docs-cn

文章轉(zhuǎn)自微信公眾號@前端之巔

上一篇:

前端 api 請求緩存方案

下一篇:

大規(guī)模分布式架構(gòu)中,怎樣設(shè)計和選擇 API 限流技術(shù)?
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費(fèi)

#AI深度推理大模型API

對比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費(fèi)