import random

導入(FastAPI)


FastAPI 的導入聲明則被歸類到不同的包中,所以看起來很復雜。下面的代碼塊導入了對表單字段輸入的支持、對返回不同類型返回的支持,以及通過模板引擎渲染靜態文件和 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

最最基礎的導入應該長這個樣子:

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 可以處理靜態文件及模板引擎,初始化代碼如下:

app = Flask(__name__)

初始化(FastAPI)


除了標準初始化流程,還需“掛載”靜態文件路徑。同樣,模板引擎渲染也需要聲明一個變量。大部分的初始化代碼都是在用基于 Pydantic 的語法創建數據模型類。

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

Hello World(Flask)


創建一個可以返回字符串的路由。

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

Hello World(FastAPI)

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

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

隨機數(Flask)


在 Flask 服務器上返回隨機生成數字 API 的代碼如下。

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

隨機數(FastAPI)


FastAPI 的代碼只需簡單修改:

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

檢查 isAlpha(Flask)


下面我們將測試一個接收名為 text 的查詢參數,并返回 JSON 結果的 API。在 Flask 中,這一步是通過路由裝飾器設置完成的。在這里我們將其設置為僅接受 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 操作需要調用 app.get 來完成;而對于多個功能相同的 HTTP 請求方法則需要將其邏輯打包到一個函數中,然后在其各自的操作中獨立調用。查詢參數需要和類型提示一起指定。text: str 表示一個需要字符串查詢參數 text。也可以通過指定默認值 text = ‘text’,使其成為一個可選參數。

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

創建新 User(Flask)


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

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

創建新 User(FastAPI)


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

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

更新 Language(Flask)


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

@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 輸入參數的類型提示。直接使用 variable_name.attribute_name 語法調用即可,解析會在后臺正確完成。在本例中,我們用 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 網頁(Flask)

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

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

HTML 網頁(FastAPI)


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

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

基于 Jinja2Templates 創建了一個變量:

templates = Jinja2Templates(directory="templates")

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

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

文件響應(Flask)

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

文件路徑

文件名

另外,你也可以額外聲明其他參數,諸如:as_attachment,通過修改 Content-Disposition 頭來指定其為附件。路徑參數可以通過<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)

文件響應(FastAPI)

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

path:需要流式傳輸的文件路徑

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

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

filename:設置后,會被響應的 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})

主函數(Flask)


Flask 中主函數應如下:

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

然后在終端中通過這條命令運行文件:

python myapp.py

主函數(FastAPI)


FastAPI 則需要導入 uvicorn。

import uvicorn

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

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

然后在終端中正常運行即可:

python myapp.py

更好的方法則是不調用主函數,在終端中通過 uvicorn 直接運行。

uvicorn myapp:app

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

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

port:服務器端口,默認為 8000。這條代碼可以將端口號改為 5000:

uvicorn myapp:app --reload --port 5000

Flask 服務器


以下為 Flask 服務器中的完整 代碼。

# 導入聲明
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!"
# 隨機數,GET 方法,返回字符串
@app.route('/random-number')
def random_number():
return str(random.randrange(100))

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

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

# 用于認證、校驗、更新數據庫
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)

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

# 文件響應,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 服務器


以下 代碼 是使用 FastAPI 實現的相同功能。

# 導入聲明
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()

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

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

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

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

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

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

# 創建新 user,POST 方法,表單字段,返回 JSON
@app.post('/create-user')
async def create_user(id: str = Form(...), name: str = Form(...)):
# 用于認證、校驗、更新數據庫
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)

# 服務網頁,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"})

# 文件響應,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)

第三步:文檔

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

http://localhost:8000/docs

進入之后你會看到以下界面:

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

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

ReDoc

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

http://localhost:8000/redoc

你會看到以下文檔界面。

第四步:結論

總結時間:

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

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

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

文章轉自微信公眾號@前端之巔

上一篇:

前端 api 請求緩存方案

下一篇:

大規模分布式架構中,怎樣設計和選擇 API 限流技術?
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

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

10個渠道
一鍵對比試用API 限時免費