How to get information from post data using fastapi - python

My script looks like this
from pydantic import BaseModel
from fastapi import FastAPI
class Credentials(BaseModel):
url:str
username:str
password:str
#app.post("/"/credentials"")
async def post_credentials(conn: Required):
return conn
#app.get("/main")
async def receive_from_post():
I would like to know if there is a way to retrieve the data from "POST" in "GET"

Related

How to write a custom FastAPI middleware class

I have read FastAPI's documentation about middlewares (specifically, the middleware tutorial, the CORS middleware section and the advanced middleware guide), but couldn't find a concrete example of how to write a middleware class which you can add using the add_middleware function (in contrast to a basic middleware function added using a decorator) there nor on this site.
The reason I prefer to use add_middleware over the app based decorator, is that I want to write a middleware in a shared library that will be used by several different projects, and therefore I can't tie it to a specific FastAPI instance.
So my question is: how do you do it?
As FastAPI is actually Starlette underneath, you could use BaseHTTPMiddleware that allows you to implement a middleware class (you may want to have a look at this post as well). Below are given two variants of the same approach on how to do that, where the add_middleware() function is used to add the middleware class. Please note that is currently not possible to use BackgroundTasks (if that's a requirement for your task) with BaseHTTPMiddleware—check #1438 and #1640 for more details. Alternatives can be found in this answer and this answer.
Option 1
middleware.py
from fastapi import Request
class MyMiddleware:
def __init__(
self,
some_attribute: str,
):
self.some_attribute = some_attribute
async def __call__(self, request: Request, call_next):
# do something with the request object
content_type = request.headers.get('Content-Type')
print(content_type)
# process the request and get the response
response = await call_next(request)
return response
app.py
from fastapi import FastAPI
from middleware import MyMiddleware
from starlette.middleware.base import BaseHTTPMiddleware
app = FastAPI()
my_middleware = MyMiddleware(some_attribute="some_attribute_here_if_needed")
app.add_middleware(BaseHTTPMiddleware, dispatch=my_middleware)
Option 2
middleware.py
from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware
class MyMiddleware(BaseHTTPMiddleware):
def __init__(
self,
app,
some_attribute: str,
):
super().__init__(app)
self.some_attribute = some_attribute
async def dispatch(self, request: Request, call_next):
# do something with the request object, for example
content_type = request.headers.get('Content-Type')
print(content_type)
# process the request and get the response
response = await call_next(request)
return response
app.py
from fastapi import FastAPI
from middleware import MyMiddleware
app = FastAPI()
app.add_middleware(MyMiddleware, some_attribute="some_attribute_here_if_needed")
A potential workaround for the BaseHTTPMiddleware bug raised by #Error - Syntactical Remorse, which seems to work for me at least, is to use partial and use a functional approach to your middleware definition:
middleware.py
from typing import Any, Callable, Coroutine
from fastapi import Response
async def my_middleware(request: Request, call_next: Callable, some_attribute: Any) -> Response:
request.state.attr = some_attribute # Do what you need with your attribute
return await call_next(request)
app.py
from functools import partial
from fastapi import FastAPI
from middleware import my_middleware
app = FastAPI()
my_custom_middleware: partial[Coroutine[Any, Any, Any]] = partial(my_middleware, some_attribute="my-app")
app.middleware("http")(my_custom_middlware)

using slowapi with a datamodel instead of request as parameter

I want to use slowapi on a fastapi python app, for implementing RateLimiter. However, The definitions of the endpoints use a data model for parameters (inheriting from BaseModel) rather than a request, as the documentation of slowapi require. here is a sample endpoint from the code:
#app.post("/process_user")
def process_user(
params: NewUser,
pwd: str = Depends(authenticate),
):
where NewUser is the data model
class NewUser(BaseModel):
...
How can I add a slowapi rate limiter with minimum change of the design of the code?
Thanks.
You need to pass a Starlette request with your Pydantic model. For example...
from fastapi import FastAPI
from pydantic import BaseModel
from slowapi import Limiter
from slowapi.util import get_remote_address
from starlette.requests import Request
limiter = Limiter(key_func=get_remote_address)
app = FastAPI()
app.state.limiter = limiter
class NewUser(BaseModel):
...
#app.post("/process_user")
#limiter.limit("1/second")
def process_user(
params: NewUser,
pwd: str = Depends(authenticate),
request: Request
):

Reading json:api filters with fast api

I'm trying to move my rest api project from flask to FastAPI
on my get function I made it very easy to get all the filters and by using flask I used the command
filter_value = request.args.get(f'filter[{filter_name}]')
where filter_name changed between all the fields i have on my object and that's how i could read urls like
http://127.0.0.1:8000/beers?filter[isbn]=72533
but now when I moved to fast api I can't find a way to read url like this.
Naming parameters in such a way, in my opinion, is not good.
Despite that, here's a working example.
from fastapi import FastAPI, Request
app = FastAPI()
#app.get("/")
async def get(req: Request):
if "filter[filter]" in req.query_params:
return req.query_params["filter[filter]"]
return ""
You have to directly access the request and manipulate it, since it is not possible to declare it as a function's parameter due to it's name. Choosing a different naming convention would allow to define the parameter as in the example below
from fastapi import FastAPI, Request
app = FastAPI()
#app.get("/")
async def get(my_parameter=None):
if my_parameter is not None:
return my_parameter
return ""

FastAPI equivalent of Flask's request.form, for agnostic forms

I try to migrate from Flask to FastAPI, and I was wondering if there is something similar to Flask's:
payload = request.form.to_dict(flat=False)
payload = {key:payload[key][0] for key in payload}
for FastAPI.
Until now I've found only some hacks, were you still had to implement one-by-one all the form's arguments to a function:
from pydantic import BaseModel
class FormData(BaseModel):
alfa: str=Form(...)
vita: str=Form(...)
async def Home(request: Request, form_data:FormData)
This example is of course better in readability than the standard form handling:
async def Home(username: str = Form(...), something_else: str = Form(...)):
But still it's quite restricting, due to the necessary declaration of all form fields.
Is there any other more agnostic & elegant approach?
Thanks in advance & I apologize if this a trivial question I've failed to find through googling :)
You can get the underlying starlette request and use its request.form() method. It requires python-multipart to work:
from fastapi import FastAPI, Request
app = FastAPI()
#app.post("/example")
async def example(request: Request):
form_data = await request.form()
return form_data
Example of calling it:
C:\>curl -X POST "http://localhost:8000/example" -d "hello=there&another=value"
{"hello":"there","another":"value"}

FastApi middleware on different folder not working

I,m getting this error when I try to run my FastApi api.
app = cls(app=app, **options)
TypeError: 'module' object is not callable
I'm trying to add a middleware on other folder separeted from main.py and don't know why isn't working. Otherwise when I add the middleware code into main.py works without problems. Here is my code, thank you for your help and excuse my english.
main.py
from fastapi import FastAPI
from fastapi import Depends, FastAPI, HTTPException
from fastapi import Request
from routers import rutas
from utils import CheckApiKey
from utils.CheckApiKey import check_api_key
app = FastAPI()
app.add_middleware(CheckApiKey, dispatch=check_api_key) <--- Here calling middleware
app.include_router(rutas.router)
if __name__ == "__main__":
import uvicorn
uvicorn.run("main:app", host="127.0.0.1", port=8000, reload=True)
Middleware
from fastapi import Request
async def check_api_key(request: Request, call_next):
print("ok")
response = await call_next(request)
return response
I found the solution...I have to create a class that inherite from starlette basehttpmiddleare, like this.
from starlette.middleware.base import BaseHTTPMiddleware
class CheckApiKey(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
print("ok")
response = await call_next(request)
return response
The CheckApiKey seems like a python module in your case and check_api_key is the middleware function.
The issue was, the add_middleware() method expects the first argument as a callable function or callable class. But in your case, you were given a module.
So,
Change your statement as,
app.add_middleware(check_api_key)

Categories

Resources