Skip to content

Failing to parse FastAPI Form using dishka inject #663

@dimanu-py

Description

@dimanu-py

I've been introducing dishka in a personal project that has an API built with FastAPI. Until now, all the endpoints that I've migrated have worked smoothly, but I am facing some issues when working with a basic authentication endpoint that emits a JWT token.

The current router and endpoint looks like this:

@authenticate_account_router.post("/login")
@inject
async def authenticate_account(
    login_form: Annotated[OAuth2PasswordRequestForm, Depends()],
    controller: FromDishka[AuthenticateAccountController],
) -> JSONResponse:
    result = await controller.authenticate(
        identification=login_form.username,
        password=login_form.password,
    )
    return FastAPIResponse.as_json(result)

When I make a request or run an acceptance test I keep getting a 422 status code with the following detail:

{
  "detail": [
    {
      "type": "model_attributes_type",
      "loc": [
        "body"
      ],
      "msg": "Input should be a valid dictionary or object to extract fields from",
      "input": "grant_type=password&username=string&password=********&scope=&client_id=string&client_secret=********"
    }
  ]
}

It looked like the @inject decorator was interfering with how FastAPI solves the Depends. Searching in the documentation, I found the section where it was explained how to integrate dishka with FastAPI Depends. Basically, we need to make a helper function annotated with the @inject too and pass that function to the Depends parameter. So I iterated the solution tryhing to keep it as simple as possible:

@inject
async def get_login_form(username: str = Form(...), password: str = Form(...)) -> OAuth2PasswordRequestForm:
    return OAuth2PasswordRequestForm(username=username, password=password)


@authenticate_account_router.post(
    "/login",
    responses={
        status.HTTP_200_OK: {"model": OkResponse},
        status.HTTP_422_UNPROCESSABLE_CONTENT: {"model": UnprocessableEntityError},
        status.HTTP_401_UNAUTHORIZED: {"model": UnauthorizedError},
    },
)
@inject
async def authenticate_account(
    login_form: Annotated[OAuth2PasswordRequestForm, Depends(get_login_form)],
    controller: FromDishka[AuthenticateAccountController],
) -> JSONResponse:
    result = await controller.authenticate(
        identification=login_form.username,
        password=login_form.password,
    )
    return FastAPIResponse.as_json(result)

But I still get a similar error:

{
  "detail": [
    {
      "type": "model_attributes_type",
      "loc": [
        "body"
      ],
      "msg": "Input should be a valid dictionary or object to extract fields from",
      "input": "username=johndoe&password=superstrongpassword"
    }
  ]
}

Is there a bug working with form requests? Or is it just not supported?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    To be released

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions