FastApi(一)

from fastapi import FastAPI, Path, Query, Cookie, Header #FastAPI是直接从 Starlette 继承的类。你可以通过 FastAPI使用所有的 Starlette 的功能
import uvicorn
from enum import Enum
from typing import Optional, List  #可选
from pydantic import BaseModel, Field
from datetime import date

app = FastAPI()   #创建一个FastApi实例,app名字任意

"""
    路径操作装饰器, 函数顺序即路由的顺序
    路径参数
    路径参数总是必需的,因为它必须是路径的一部分
"""
#1、路径参数,函数参数的名称要跟路径url的参数一样
@app.get('/woody/{name}')
async def read_item(name):  #item_id是url传递的参数
    return {"name": name}

#2、路径参数,有类型
@app.get('/woody/{user_id}')
async def read_user(user_id: int):  #指定为int类型
    return {"user_id": user_id}

#3、路径参数为枚举类型
class ModelName(str, Enum):
    morning = "早上"
    noon = "中午"
    night = "晚上"

@app.get('/woody/{model_name}')
async def get_model(model_name: ModelName):  #带有类型标注的路径参数
    if model_name == ModelName.morning:      #于枚举成员进行比较,可使用your_enum_member.value 来获取实际的值
        return {"model_name": model_name, "message": "哈哈哈"}

    if model_name == ModelName.noon:
        return {"model_name": model_name, "message": "嘿嘿嘿"}

    return {"model_name": model_name, "message": "哦哦哦"}

#4、路径参数包含路径(即参数带有/),使用/files/{file_path:path}
@app.get('/files/{file_path: path}')   #:path 说明该参数应匹配任意的路径
async def read_file(file_path: str):
    return {"file_path": file_path}

#5、路径参数,长度正则表达式的校验
@app.get('/path/{num}')
async def read_num(num: int = Path(..., title='Path类用于校验路径参数', description='2021314', ge=1, le=10)):
    return num

"""
    查询参数,声明不属于路径参数的其他函数参数时,它们将被自动解释为'查询字符串'参数;
    由于查询参数不是路径的固定部分,因此它们可以是可选的,并且可以有默认值。
"""
#1、查询参数, 查询字符串是键值对的集合,这些键值对位于 URL 的 ? 之后,并以 & 符号分隔
@app.get('/query')
async def page_limit(page: int = 1, limit: Optional[int] = None):  #有默认值则可选
    if limit:
        return {"page": page, "limit": limit}
    return {"page": page}

# 2、查询参数, bool类型转换
#bool [1,True,true, on, yes,或这几个的变体形式(大写,首字母大写等等)]对应true
@app.get("/query/bool")
async def type_conversion(param: bool = False, q: Optional[str] = None):
    return param, q

# 3、查询参数, 字符串校验
#必填参数,Query(...,)第一个位置写三个点; Query(None,)选填参数;Query(A,) 默认值A
@app.get("/query/string")
async def check_string(value: str = Query(..., min_length=1, max_length=10, regex='^a'),
                       values: List[str] = Query(default=["v1", "v2"], alias="哈哈哈哈")):  #alias取别名
    return value, values

"""
    使用 Pydantic 模型来声明请求体
    不能使用 GET 操作发送请求体。
    要发送数据,你必须使用下列方法之一:POST(较常见)、PUT、DELETE 或 PATCH
    使用 Body 指示 FastAPI 将其作为请求体的另一个键进行处理
"""
class CityInfo(BaseModel):
    name: str = Field(..., example="Beijing")  #必填,Field第一个参数为三个点, example注解作用
    country: str
    country_code: str = None
    country_population: int = Field(default=800, title="人口数量", description="国家的人口数量", ge=800)

    class Config:
        schema_extra = {  #给的例子
            "example": {
                "name": "wuhan",
                "country": "China",
                "country_code": "CN",
                "country_population": 140000
            }
        }

@app.post('/request_body/city')
async def city_info(city: CityInfo):
    return city.dict()

#mix
@app.put("/request_body/city/{name}")
async def mix_city_info(name: str, city01: CityInfo,
                        city02: CityInfo,
                        confirmed: int = Query(ge=0, description='确诊数', default=0),
                        death: int = Query(ge=0, description='死亡数', default=0)
):
    if name == 'wuhan':
        return {"wuhan": {"confirmed": confirmed, "death": death}}
    return city01.dict(), city02.dict()

#嵌套
class Data(BaseModel):
    city: List[CityInfo] = None
    date: date
    confirmed: int = Query(ge=0, description='确诊数', default=0)
    death: int = Query(ge=0, description='死亡数', default=0)
    recovered: int = Query(ge=0, description='痊愈数', default=0)

@app.put("request_body/nested")
async def nested_modle(data: Data):
    return data

#Cookie和Header
@app.get('/cookie')
async def get_cookie(cookie_id: Optional[str] = Cookie(None)):
    return {"cookie_id": cookie_id}

@app.get('/header')
async def get_header(user_agent: Optional[str] = Header(None, convert_underscores=True), x_token: List[str] = Header(None)):
    return {"User-Agent": user_agent, "Token": x_token}

if __name__ == '__main__':
    uvicorn.run('run:app', host='0.0.0.0', port=8000, reload=True, debug=True, workers=1)  #app即fastapi实例的名称

函数参数将依次按如下规则进行识别:

  • 如果在路径中也声明了该参数,它将被用作路径参数。
  • 如果参数属于单一类型(比如 int、float、str、bool 等)它将被解释为查询参数。
  • 如果参数的类型被声明为一个 Pydantic 模型,它将被解释为请求体

传递 * 作为函数的第一个参数。Python 不会对该 * 做任何事情,但是它将知道之后的所有参数都应作为关键字参数(键值对),也被称为 kwargs,来调用。即使它们没有默认值。

async def update_item(
    *,
    item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
    q: Optional[str] = None,
    item: Optional[Item] = None,
):
	pass
	
importance: int = Body(...) #单一值
item: Item = Body(..., embed=True)
#FastAPI 在仅声明了一个请求体参数的情况下,将原本的请求体嵌入到一个键中

实战:
使用 Config 和 schema_extra 为Pydantic模型声明一个示例;
在 Field, Path, Query, Body等使用example注解;

import requests
import json
import time
import random
import ast
import uvicorn
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field, HttpUrl
from typing import Optional, List
from fastapi.encoders import jsonable_encoder
from enum import Enum

class SearchModel(BaseModel):
    project: str = Field(..., title='项目名称', description="需在fastdev中已支持")  #必填
    date: List[str] = Field(None, title='日期', description="日期区间")           #其实是必填的
    inbound_request: Optional[str] = Field(None, title="接口请求", description="pNewOrder bussiness_id=260")  #选填
    inbound_response: Optional[str] = Field(None, title="接口返回", description="errno:0")
    outbound_request: Optional[str] = Field(None, title="下游请求", description="下游接口及请求参数")
    outbound_response: Optional[str] = Field(None, title="下游返回", description="下游返回值")
    apollo: Optional[str] = Field(None, title="Apollo", description='apollo开关名称')
    page: Optional[int] = 1
    size: Optional[int] = 10
    session_id: Optional[str] = Field(None, title="SessionId")
    dsl: Optional[str] = Field(None, title="快速查询")

    class Config:
        schema_extra = {  # 给的例子
            "example": {
                ……
            }
        }

class SenceModel(BaseModel):
    url: HttpUrl   #必填,查询接口的url和回放接口的URL
    params: SearchModel   #嵌套

    class Config:
        schema_extra = {  # 给的例子
            "example": {
               ……
            }
        }

app = FastAPI()

def get_current_date():
    """获取当前日期"""
    date = time.strftime('%Y-%m-%d', time.localtime(time.time()))
    return date

def get_current_time():
    """获取当前时间"""
    now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
    return now

def project_name_list():
    """
    对应查询的接口的project
    :return:
    """
    url = 'xxxx'
    response = requests.get(url).json()
    if response['errmsg'] == 'success':
        project_name_list = get_all_values(response['names'], 'name')
    return project_name_list

def split_str_to_list(string):
    """
    splitlines() 按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表, 空白行为''
    :return:
    """
    split_string_to_list = string.splitlines()  # 返回一个包含各行作为元素的列表
    list_filter_null = list(filter(None, split_string_to_list))  # filter去掉列表中的''
    return list_filter_null

def convert_str_to_dict(string):
    """
    输入:'{"name" : "john", "gender" : "male", "age": 28}'
    输出: {"name" : "john", "gender" : "male", "age": 28}
    :param string:
    :return:
    """
    convert_result = ast.literal_eval(string)
    return convert_result

def get_all_values(data, key):
    """
    遍历嵌套字典的列表,取出所有key对应的值
    :param data:列表,列表元素为字典
    :return:
    """
    if not isinstance(key, str):
        raise TypeError('key must be str')

    key_value_list = []
    if isinstance(data, list) and data != []:
        for i in range(len(data)):
            if isinstance(data[i], dict):
                if key in data[i].keys():
                    key_value_list.append(data[i][key])
    return key_value_list

@app.post('/woody/sence/')
async def fast_dev_replayed(sence: SenceModel):
    search_url = sence.url + '/xx/'
    replayed_url = sence.url + '/yy/'

    project_list = project_name_list()
    default_period = [get_current_date(), get_current_date()]

    if sence.params.project not in project_list:  #project必须是fastdev已支持的
        raise HTTPException(status_code=404, detail= "project is not supported right now!")

    if not sence.params.date:                     #时间为空,则默认当天
        sence.params.date = default_period

    headers = {'Content-Type': 'application/json'}
    params = jsonable_encoder(sence.params)  #将Pydantic模型转换为与JSON兼容的数据(例如dict,list等)

    response = requests.post(search_url, data=json.dumps(params), headers=headers).json()  # json.dumps()将python对象编码成json字符串

    if response["errmsg"] == "":
        if response["results"] == []:
            raise HTTPException(status_code=404, detail="查询结果为空,不存在符合条件的sessionId")

        sessionId_list = get_all_values(response["results"], "sessionId")  # 所有的sessionId
      
    if sessionId_list == []:
        raise HTTPException(status_code=404, detail="不存在……")

    # for i in range(len(sessionId_list)):  #TODO 暂时写死取sessionId_list[0]
    result = fast_dev_replay(replayed_url, params["project"], sessionId_list[0])
    return {"sence_fastdev": result}

def fast_dev_replay(replay_url, project, sessionId):
    if not isinstance(sessionId, str):
        raise TypeError('sessionId类型错误')

    url = replay_url + sessionId
    data = {'project': project}
    response = requests.get(url, params=data).json()

    if response['success'] == False:  
        return response['diffs']
    elif response['success'] == True:
        return response

if __name__ == '__main__':
    uvicorn.run('run:app', host='0.0.0.0', port=8000, reload=True, debug=True, workers=1)  #app即fastapi实例的名称
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值