flask - fastapi    (python 异步API 框架  可以自动生成swagger 文档)  常用示例:

之前使用 flask 需要手动写文档, 这个可以自动生成, 

fastapi 0.82.0
pydantic 1.10.2
python-multipart 0.0.5
uvicorn 0.18.3

 swagger-ui      http://127.0.0.1:5555/docs      

参数可选:

@app.post("/blog/add", tags=["blog"])  # 指定命名空间
async def add_blog_api(content: str = Form(...),
                       operator: str = Form(...),
                       # img: UploadFile = File(...)):
                       img: Optional[UploadFile] = None): # 可选参数
    img_url = None
    if img:
        out_img_dir = compress_img(img.file, quality=20)
        img_url = upload_file(url=UPLOAD_FILE_URL, file_path=out_img_dir)
        print("img url " + str(img_url))
        os.remove(path=out_img_dir)
        print(img_url)
    blog = add_blog(content=content, img=img_url, operator=operator)
    print("post data " + str(blog))
    return str(blog)

 

 

常用示例 (python3.8 )

 pip install fastapi
 pip install uvicorn
 pip install python-multipart
添加跨域:
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI(title="Sea test API")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

 

 
# -*- coding:utf-8 -*-
# file_name : base_test.py
# Author : Sea
# Time : 2022-09-06 15:03:12
# pip install fastapi
# pip install uvicorn
# pip install python-multipart
############################################
from typing import Optional, Union

from fastapi import FastAPI, UploadFile, File, Query, Form, Request
import uvicorn
from pydantic import BaseModel

app = FastAPI()


#  ************ get method ***************
@app.get("/")
async def index(xx: str, req: Request):
    print(" 可以通过request 获取一切参数 xx" + str(req.query_params))  # xxxx=13
    print(" 可以通过request 获取一切参数 xx" + str(req.form()))  
    print(" 可以通过request 获取一切参数 xx" + str(req.headers))  
    return {"name": "哈哈哈"}


@app.get("/user/{user_id}")
async def get_user(user_id: Union[int, str], name: Optional[str] = None):
    """通过 Union 来声明一个混合类型,int 在前、str 在后。会先按照 int 解析,解析失败再变成 str
       然后是 name,它表示字符串类型、但默认值为 None(不是字符串),那么应该声明为 Optional[str]"""
    return {"user_id": user_id, "name": name}


# Query 里面除了限制最小长度和最大长度,还有其它的功能
@app.get("/user", tags=["user"])
async def check_length(
        password: str = Query(default="satori", min_length=6, max_length=15, regex=r"^satori")
):
    """此时的 password 默认值为 'satori',并且传递的时候必须要以 'satori' 开头
       但是值得注意的是 password 后面的是 str,不再是 Optional[str],因为默认值不是 None 了
       当然这里即使写成 Optional[str] 也是没有什么影响的
    """
    return {"password": password}


#  ******  post  method *********
class Car(BaseModel):
    name: str  # //必选参数
    weight: Optional[str] = None  # //可选参数
    price: float  # //必选参数
    length: Optional[float] = None


#  ******文件上传 post json ******
@app.post("/car/", tags=["car"])
async def create_item(car: Car):
    return car


# 表单提交 + 文件上传  # 如果要上传多个文件 files: List[bytes] = File(...) or  files: List[UploadFile] = File(...)):
@app.post("/files")
async def create_file(
        file1: bytes = File(...),
        file2: UploadFile = File(...),
        token: str = Form(...),
        username: str = Form(...),  # 表单
        password: str = Form(...)):
    file = file2.file
    return {
        "filesize": len(file1),
        "token": token,
        "oen_content_type": file2.content_type
    }


#   ******  put  method  ******
class Item(BaseModel):
    name: str
    description: Optional[str] = None
    price: float
    tax: Optional[float] = None


@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: Optional[str] = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result


if __name__ == "__main__":
    # 启动服务,因为我们这个文件叫做 base_test.py,所以需要启动 base_test.py 里面的 app
    # 第一个参数 "base_test:app" 就表示:当前文件名字 ,然后是 host 和 port 表示监听的 ip 和端口
    uvicorn.run(app, host="0.0.0.0", port=5555)

 

文件上传内存优化: 

def add_blog_api(content: str, operator: str, img: UploadFile = File(None)):     File(None) :表示可选(swagger)
@app.post("/uploadfile/")
async def upload_file(tempfile: UploadFile = File(...)):
    with open(f"/opt/{tempfile.filename}", 'wb') as f:
        for i in iter(lambda : tempfile.file.read(1024*1024*10),b''):
            f.write(i)
    f.close()
    return {"file_name":tempfile.filename}
# 上传多个文件

app.post("/uploadfiles/")
async def upload_file(tempfiles: List[UploadFile] = File(...)):
    for tempfile in tempfiles:
        with open(f"/opt/{tempfile.filename}", 'wb') as f:
            for i in iter(lambda : tempfile.file.read(1024*1024*10),b''):
                f.write(i)
        f.close()
    return {"files_name":[x.filename x in tempfiles]}
from fastapi import FastAPI, UploadFile, File
@app.post(path="/user/add", tags=["User"])
async def add_user_api(username: str, avatar: UploadFile = File(...)):
    image_path = "./img/" + str(uuid.uuid1()) +".png"
    with open(image_path, "wb") as a:
        a.write(await avatar.read())
        
    out_img_dir = compress_img(image_path, quality=20)
    avatar = upload_file(url=UPLOAD_FILE_URL, file_path=out_img_dir)
    os.remove(path=out_img_dir)
    os.remove(path=image_path)
    print(avatar)
    user = add_user(username=username, avatar=avatar)
    return {"result": user}

 

文件下载:

from fastapi import FastAPI, FileResponse  
app = FastAPI()  
@app.get("/download")  
async def download_file():  
    filename = "example.txt"  # 文件名  
    file_path = f"files/{filename}"  # 文件路径  
    return FileResponse(file_path, filename=filename)

 

 

 

get  post   body  header  ...

class Item(BaseModel):
    id: str
    title: str

@app.post("/body")
async def get_body(item: Item, request: Request):
    myjson = await request.json()  # dict
    res = {
        # 获取 Request Body
        "body": await request.json(),
        "body_bytes": await request.body()
    }
    print(type(myjson))
    return res

 

 统一数据检验

#!/usr/bin/env python3
import time
from typing import Optional
import uvicorn
from fastapi import FastAPI, Request
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from fastapi.responses import JSONResponse
# -------------------------  API  ------------------------------
app = FastAPI(title="Sea test API")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/airline/test", tags=["Sea"])
def base_test_api():
    return {"你好": "今天天气不错!"}


"""
统一异常处理 (数据校验)
"""
@app.exception_handler(RequestValidationError)
async def request_validation_exception_handler(request: Request, exc: RequestValidationError):
    print(f"参数不对{request.method} {request.url}")
    return JSONResponse({"code": "2003", "message": exc.errors()})


class FlightInfo(BaseModel):
    flight: str  # 必填
    origin: Optional[str] = None  # 可选参数
    destination: str
    etd: str
    eta: str = None


@app.post("/airline/add_flight", tags=["Sea"])
async def add_flight_schedule(flight: FlightInfo, req: Request):
    if flight:
        body = await req.json()
        print(body)
        body["_id"] = time.strftime('%Y-%m-%d') + str(flight.flight)
        body["ts"] = int(time.time())
        body["query_date"] = time.strftime('%Y-%m-%d %T')
    return {"code": "200", "message": "ok"}



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

 

 

数据嵌套 LIst[Bean]

from typing import Optional, Union, List
import uvicorn
from fastapi import FastAPI, Body
from pydantic import BaseModel, HttpUrl
app = FastAPI()
class Image(BaseModel):
    name: str
    url: HttpUrl
class Person(BaseModel):
    name: str
    desc: Optional[str] = None
    year: List[int]
    img: Union[List[Image], None] = None 
@app.post("/item")
async def create_item(person: Person = Body(embed=True)):
    return person
if __name__ == '__main__':
    uvicorn.run(app=app, host="0.0.0.0", debug=True)

 

 

 

整合eureka (pip install py_eureka_client)

 

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @mail    : lshan523@163.com
# @Time    : 2022/9/8 15:53
# @Author  : Sea
# @File    : fastapi_euraka.py
# @history:
#     pip install py_eureka_client
# ****************************
import base64
import uvicorn
from fastapi import FastAPI
from fastapi.responses import FileResponse
import py_eureka_client.eureka_client as eureka_client
import socket


# 获取ip
def extract_ip():
    st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        ip = st.getsockname()[0]
    except Exception:
        ip = '127.0.0.1'
    finally:
        st.close()
    return ip


server_port = 9090
server_host = extract_ip()
# 配置eureka
eureka_client.init(eureka_server="http://root:root@hadoop002:8761",
                   app_name="fastapi_euraka-app",
                   instance_port=server_port,
                   instance_host=server_host)

app = FastAPI(title="Sea test python euraka")


@app.get("/hello/{name}")
def say_hello(name: str):
    return {"message": f"Hello {name}"}


@app.post("/picture")
def get_picture():
    return FileResponse('/home/sea/img/test.png')


# return ase64
@app.get("/base64img")
def get_picture():
    with open('/home/sea/img/test.png', mode='rb') as file:
        read = file.read()
        image_base64 = base64.standard_b64encode(read)
    return {"image": str(image_base64)}


if __name__ == '__main__':
    eureka_client.get_client()
    uvicorn.run(app, host="0.0.0.0", port=5000)

 

 

 

 

打开swagger文档:

127.0.0.1:5555/docs

 

 

整合nacos:

import nacos
import uvicorn
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from suds.client import Client
from suds.transport.https import HttpAuthenticated
 
client=nacos.NacosClient('172.16.244.200:8848',namespace='8b6f08b7-b09c-4181-bcac-1505fba5e1aa')
 
async def beat():
    client.add_naming_instance('fastapi-service','172.16.244.10',8000,group_name='dev')
    
 
# 微服务注册nacos
def register_nacos():
    client.add_naming_instance('fastapi-service','172.16.244.10',8000,group_name='dev')
 
 
app=FastAPI()
 
# 微服务注册
register_nacos()
 
 
@app.on_event('startup')
def init_scheduler():
 
    scheduler = AsyncIOScheduler(timezone="Asia/Shanghai")
    scheduler.add_job(beat, 'interval', seconds=5)
    scheduler.start()
 
@app.get('/sap/materials')
async def sap_materials():
   ''' 功能实现'''
  return JSONResponse({'code':1000,'msg':'succ','data':data},status_code=200)

要注意的几点

group_name要与其他java微服务配置要一致 写的不正确 spring cloud gateway 提示404错误

timezone='Asia/Shangai'  不加启动时会提示时区用utc

posted on 2022-09-06 15:29  lshan  阅读(1045)  评论(0编辑  收藏  举报