Rumah >pembangunan bahagian belakang >Tutorial Python >Bagaimana untuk Mencatat Badan Permintaan dan Respons HTTP Mentah dengan Cekap dalam FastAPI?

Bagaimana untuk Mencatat Badan Permintaan dan Respons HTTP Mentah dengan Cekap dalam FastAPI?

DDD
DDDasal
2024-11-30 19:08:17837semak imbas

How to Efficiently Log Raw HTTP Request and Response Bodies in FastAPI?

Cara Merekod Permintaan dan Respons HTTP Mentah dalam Python FastAPI

Pengenalan:

Dalam untuk memenuhi keperluan pengauditan untuk perkhidmatan web berasaskan Python FastAPI anda, anda perlu mengekalkan badan JSON mentah bagi kedua-dua permintaan dan respons pada laluan tertentu. Panduan ini akan membentangkan dua penyelesaian yang berdaya maju untuk mencapainya tanpa menjejaskan masa tindak balas yang ketara, walaupun semasa bekerja dengan saiz badan yang lebih kurang 1MB.

Pilihan 1: Penggunaan Middleware

Mekanik Perisian Tengah:

Perisian Tengah berfungsi sebagai penjaga pintu untuk permintaan memasuki aplikasi. Ia membenarkan untuk mengendalikan permintaan sebelum pemprosesan titik akhir dan respons sebelum kembali kepada pelanggan. Anda boleh mewujudkan middleware menggunakan @app.middleware penghias pada fungsi:

Permintaan dan Pengurusan Badan Respons:

Untuk mengakses badan permintaan daripada strim dalam middleware ( menggunakan request.body() atau request.stream()), anda perlu menyediakannya kemudian dalam kitaran permintaan-tindak balas. Siaran yang dipautkan membincangkan penyelesaian ini, yang kini tidak diperlukan untuk FastAPI versi 0.108.0 dan ke atas.

Untuk badan respons, anda boleh meniru teknik yang digariskan dalam siaran ini untuk menggunakan dan mengembalikan badan secara langsung, memberikan status kod, pengepala dan jenis media bersama-sama dengan respons asal.

Pengelogan Data:

Gunakan BackgroundTask untuk log data, memastikan pelaksanaannya selepas respons selesai. Ini menghapuskan klien menunggu untuk tugasan pengelogan dan mengekalkan integriti masa tindak balas.

Pilihan 2: Pelaksanaan APIRoute Tersuai

Laluan API Tersuai:

Pilihan ini melibatkan penciptaan kelas APIRoute tersuai untuk memanipulasi badan permintaan dan tindak balas sebelum memproses titik akhir atau mengembalikan keputusan kepada pelanggan. Ia membolehkan pengasingan pengendalian laluan tersuai ke titik akhir tertentu dengan menggunakan APIRouter khusus:

Pertimbangan:

Kekangan Memori:

Kedua-dua pendekatan mungkin menghadapi cabaran dengan permintaan atau badan tindak balas yang besar melebihi RAM pelayan yang tersedia. Menstrim respons besar boleh memperkenalkan kelewatan pihak klien atau ralat proksi terbalik. Hadkan penggunaan perisian tengah kepada laluan tertentu atau kecualikan titik akhir dengan respons penstriman yang besar untuk mengelakkan isu yang berpotensi.

Kod Contoh (Pilihan 1):

from fastapi import FastAPI, APIRouter, Response, Request
from starlette.background import BackgroundTask
from fastapi.routing import APIRoute
from starlette.types import Message
from typing import Dict, Any
import logging


app = FastAPI()
logging.basicConfig(filename='info.log', level=logging.DEBUG)


def log_info(req_body, res_body):
    logging.info(req_body)
    logging.info(res_body)



# Not required for FastAPI >= 0.108.0
async def set_body(request: Request, body: bytes):
    async def receive() -> Message:
        return {'type': 'http.request', 'body': body}
    request._receive = receive


@app.middleware('http')
async def some_middleware(request: Request, call_next):
    req_body = await request.body()
    await set_body(request, req_body)  # Not required for FastAPI >= 0.108.0
    response = await call_next(request)
    
    res_body = b''
    async for chunk in response.body_iterator:
        res_body += chunk
    
    task = BackgroundTask(log_info, req_body, res_body)
    return Response(content=res_body, status_code=response.status_code, 
        headers=dict(response.headers), media_type=response.media_type, background=task)


@app.post('/')
def main(payload: Dict[Any, Any]):
    return payload

Contoh Kod (Pilihan 2):

from fastapi import FastAPI, APIRouter, Response, Request
from starlette.background import BackgroundTask
from starlette.responses import StreamingResponse
from fastapi.routing import APIRoute
from starlette.types import Message
from typing import Callable, Dict, Any
import logging
import httpx


def log_info(req_body, res_body):
    logging.info(req_body)
    logging.info(res_body)

       
class LoggingRoute(APIRoute):
    def get_route_handler(self) -> Callable:
        original_route_handler = super().get_route_handler()

        async def custom_route_handler(request: Request) -> Response:
            req_body = await request.body()
            response = await original_route_handler(request)
            tasks = response.background
            
            if isinstance(response, StreamingResponse):
                res_body = b''
                async for item in response.body_iterator:
                    res_body += item
                  
                task = BackgroundTask(log_info, req_body, res_body)
                response = Response(content=res_body, status_code=response.status_code, 
                        headers=dict(response.headers), media_type=response.media_type)
            else:
                task = BackgroundTask(log_info, req_body, response.body)
            
            # Check if the original response had background tasks already attached to it
            if tasks:
                tasks.add_task(task)  # Add the new task to the tasks list
                response.background = tasks
            else:
                response.background = task
                
            return response
            
        return custom_route_handler


app = FastAPI()
router = APIRouter(route_class=LoggingRoute)
logging.basicConfig(filename='info.log', level=logging.DEBUG)


@router.post('/')
def main(payload: Dict[Any, Any]):
    return payload


@router.get('/video')
def get_video():
    url = 'https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4'
    
    def gen():
        with httpx.stream('GET', url) as r:
            for chunk in r.iter_raw():
                yield chunk

    return StreamingResponse(gen(), media_type='video/mp4')


app.include_router(router)

Penyelesaian ini menyediakan kaedah yang cekap untuk pembalakan permintaan HTTP mentah dan badan tindak balas dalam FastAPI tanpa menjejaskan masa respons dengan ketara.

Atas ialah kandungan terperinci Bagaimana untuk Mencatat Badan Permintaan dan Respons HTTP Mentah dengan Cekap dalam FastAPI?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn