Myzel394
Myzel394

Reputation: 1317

How to add custom validation in FastAPI?

I'm building a sort of Youtube audio downloader api and I would like to validate video ids (using youtube_dl). How do I add custom validation in FastAPI?

@router.get(
    "/audio/{video_id}",
    response_description="Redirects to the static url of the audio file.",
)
async def download_audio(
    video_id: str = Path(None, title="ID of the video (what you can see in the url)"),  # <-- How to validate?
):
...
# Here's some logic to download the audio of the video and save it. After that a `RedirectResponse` gets returned.

I know I could validate it in the function, but I think FastAPI has a better alternative.

Upvotes: 5

Views: 13490

Answers (1)

HTF
HTF

Reputation: 7260

FastAPI is using pydantic for data validations so you can either use Standard Library Types, Pydantic Types or Constrained Types for path params.

Example:

from fastapi import FastAPI
from pydantic import constr, NegativeInt


app = FastAPI(title="Test")


@app.get("/01/{test}")
async def test01(test: NegativeInt):
    return {"test": test}


@app.get("/02/{test}")
async def test02(test: constr(regex=r"^apple (pie|tart|sandwich)$")):
    return {"test": test}

Test:

$ curl -Ss localhost:8000/01/1 | python -m json.tool
{
    "detail": [
        {
            "loc": [
                "path",
                "test"
            ],
            "msg": "ensure this value is less than 0",
            "type": "value_error.number.not_lt",
            "ctx": {
                "limit_value": 0
            }
        }
    ]
}
$ curl -Ss localhost:8000/01/-1 | python -m json.tool
{
    "test": -1
}


$ curl -Ss localhost:8000/02/-1 | python -m json.tool
{
    "detail": [
        {
            "loc": [
                "path",
                "test"
            ],
            "msg": "string does not match regex \"^apple (pie|tart|sandwich)$\"",
            "type": "value_error.str.regex",
            "ctx": {
                "pattern": "^apple (pie|tart|sandwich)$"
            }
        }
    ]
}
$ curl -Ss localhost:8000/02/apple%20pie | python -m json.tool
{
    "test": "apple pie"
}

That having been said, I guess your best bet is a RegEx, something like constr(regex=r"^[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]$"), see Format for ID of YouTube video for details.

Update Thu 22 Apr 22:16:14 UTC 2021:

You can try something like this (this is just an example of course):

from __future__ import unicode_literals

import youtube_dl

from fastapi import FastAPI, HTTPException
from fastapi.concurrency import run_in_threadpool
from fastapi.responses import FileResponse


URL = "https://www.youtube.com/watch?v="


app = FastAPI(title="Test")

ydl_opts = {
    "format": "bestaudio/best",
    "outtmpl": "%(id)s.%(ext)s",
    "postprocessors": [
        {
            "key": "FFmpegExtractAudio",
            "preferredcodec": "mp3",
            "preferredquality": "192",
        }
    ],
    "quiet": True,
}


def get_audio(video_id: str):
    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        try:
            yinfo = ydl.extract_info(f"{URL}{video_id}")
        except youtube_dl.DownloadError:
            ret = None
        else:
            ret = (f"{yinfo['title']}.mp3", f"{yinfo['id']}.mp3")

    return ret


@app.get("/audio/{video_id}")
async def download_audio(video_id: str):
    ret = await run_in_threadpool(get_audio, video_id)

    if not ret:
        raise HTTPException(status_code=418, detail="Download error or invalid ID")

    title, filename = ret

    return FileResponse(filename, filename=title)

Test:

$ time curl -Ss -D - -o rickroll.mp3 localhost:8000/audio/dQw4w9WgXcQ
HTTP/1.1 200 OK
date: Thu, 22 Apr 2021 22:26:50 GMT
server: uvicorn
content-type: audio/mpeg
content-disposition: attachment; filename*=utf-8''Rick%20Astley%20-%20Never%20Gonna%20Give%20You%20Up%20%28Video%29.mp3
content-length: 5090733
last-modified: Mon, 13 Jan 2020 17:04:18 GMT
etag: e26e47edb1401e6e65e4c8eb221f3419


real    0m11.883s
user    0m0.047s
sys 0m0.057s
$ file rickroll.mp3 
rickroll.mp3: Audio file with ID3 version 2.4.0, contains:MPEG ADTS, layer III, v1, 192 kbps, 48 kHz, Stereo

Upvotes: 5

Related Questions