Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hx and page decorators eat the JSON body of POST requests #68

Closed
signed-log opened this issue Mar 20, 2025 · 7 comments
Closed

hx and page decorators eat the JSON body of POST requests #68

signed-log opened this issue Mar 20, 2025 · 7 comments
Labels
question Further information is requested

Comments

@signed-log
Copy link

#!/usr/bin/env python3

import pydantic
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fasthx import Jinja

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
  exc_str = f"{exc}".replace("\n", " ").replace("   ", " ")
  print(f"{request}: {exc_str}")
  content = {"status_code": 10422, "message": exc_str, "data": None}
  return JSONResponse(content=content, status_code=status.HTTP_422_UNPROCESSABLE_ENTITY)

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
G_TEMPLATES = Jinja2Templates(directory="templates")
fasthx_jinja = Jinja(G_TEMPLATES)


class A(pydantic.BaseModel):
  arg_a: str

class B(pydantic.BaseModel):
  arg_b: str

@app.post("/a")
@fasthx_jinja.hx("a.html.j2")
async def new_compose_docker(input_model: A) -> list[B]:
  return [B(arg_b=input_model.arg_a)]

When using the decorator it fails with a 422 error stating that input_model is None (sorry, I can't really provide a HTML boilerplate right now, it is a simple form) :

<starlette.requests.Request object at 0x7f294ed884d0>: [{'type': 'missing', 'loc': ('query', 'request'), 'msg': 'Field required', 'input': None}, {'type': 'missing', 'loc': ('query', 'input_model'), 'msg': 'Field required', 'input': None}]

The request is correct as it is processed correctly without the decorator

System info

OS: Debian 12 Python: Python 3.12.6 fasthx==2.3.0 fastapi==0.115.8
@signed-log signed-log changed the title @hx() decorator eats the JSON body of POST request hx and page decorators eat the JSON body of POST requests Mar 20, 2025
@volfpeter
Copy link
Owner

volfpeter commented Mar 20, 2025

Hi,

The issue is probably in your template or a Jinja context processor for example. It's hard to say without seeing the full code.

The decorators definitely do not consume the payload, they just forwards the resolved dependencies (including the payload) to actual route implementation, then execute the rendering using the request, the route's result, and its resolved dependencies (depending on how the Jinja context is created).

You can test it like this:

Create a templates folder and put hello.jinja in it with this content:

Hello {{ value }}!

Create a simple app next to the templates folder, e.g. app.py:

import os

from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel

from fasthx import Jinja


class Data(BaseModel):
    value: str

basedir = os.path.abspath(os.path.dirname(__file__))
app = FastAPI()
templates = Jinja2Templates(directory=os.path.join(basedir, "templates"))
jinja = Jinja(templates)

@app.post("/hello")
@jinja.hx("hello.jinja")
async def hello(data: Data) -> Data:
    return data

Start the app with uvicorn app:app --reload, open http://127.0.0.1:8000/docs in your browser, and send a post request to the /hello route with the hx-request header set to true, this will be the response:

Image

Without the hx-request header, the route will simply return the payload, as expected.

(Edit: I used the latest version of FastAPI, currently 0.115.11)

@signed-log
Copy link
Author

signed-log commented Mar 20, 2025

Thank you very much for your answer, very much appreciated.

Your example works, I don't know why my work won't. I just upgraded FastAPI to 0.115.11 without luck.

The Starlette error does not trigger without the decorator or if the decorator is before app.post(). But of course with the decorator before it won't work (as expected). And I checked that the error happened before any code in my function is executed.

I do not use any fasthx custom rendering or anything, and I was indeed puzzled by the fact that the Request was just passed without any consumption. Get requests with URL variables work.

Trying to breakpoint the hx decorator itself has the error thrown before any is executed.

@volfpeter
Copy link
Owner

I could help you better with access to your actual code (contact info is in the readme).
To be honest, I never encountered such an issue in any of the projects I worked on, so I really don't think this is a bug in FastHX.

@signed-log
Copy link
Author

signed-log commented Mar 20, 2025

Sadly I can't really share any code,

I am going to investigate further on my side. There are also VERY weird issues when using the decorators with Pydantic and /docs generation failing about class-not-fully-defined (that again doesn't happen with your example or without the decorator).

Thanks a lot, I will come back if I find anything

@volfpeter
Copy link
Owner

Do you mind if I close the issue? Feel free to re-open it if the bug is fasthx.

@volfpeter volfpeter added the question Further information is requested label Mar 20, 2025
@signed-log signed-log closed this as not planned Won't fix, can't repro, duplicate, stale Mar 20, 2025
@volfpeter
Copy link
Owner

@signed-log Just out of curiosity: have you managed to find the issue?

@signed-log
Copy link
Author

No, I was on a time crunch so I ended up just sending TemplateResponse manually

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants