Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Body is always parsed as JSON regardless of media_type #1018

Closed
jbkoh opened this issue Feb 22, 2020 · 21 comments
Closed

Body is always parsed as JSON regardless of media_type #1018

jbkoh opened this issue Feb 22, 2020 · 21 comments

Comments

@jbkoh
Copy link

jbkoh commented Feb 22, 2020

Describe the bug

This is to continue the discussion at #579 (comment). Body accepts an argument media_type, but regardless of the media_type Body is parsed as JSON. This is a problem for other media_types such as plain/text, application/sql, etc. And this is not compliant to OpenAPI content. Body should be able to represent plain text as str too.

Basically I wish I could do this:

request: str = Body(..., media_type='text/plain')

However, this produces 400 parsing error because Body is parsed as JSON anyway.

To Reproduce

  1. Create a file with:
from fastapi import FastAPI, Body

app = FastAPI()

@app.post("/")
def read_root(body: str = Body(..., media_type='text/plain')):
    return {"I received:": body}
  1. Run the server.

  2. Then execute: curl -d 'I want to print this' -H 'Content-Type: plain/text' http://localhost:8000

  3. It returns {"detail":"There was an error parsing the body"}

  4. But I expected it to return {"I received": "I want to print this"}.

Expected behavior

My expectation is that the Body should be parsed based on the Content-Type header always.

Screenshots

I think the information is quite comprehensive without screenshots.

Environment

  • OS: Ubuntu 18.04
  • FastAPI Version: 0.49.0
  • Python 3.7.4

Additional context

The associated part is:

if body_bytes:

This does not check Content-Type. My proposal is to fix it as

if body_bytes and request.headers['Content-Type'] == 'application/json':
# if body_bytes and request.headers.get('Content-Type', 'application/json'): # if the content type should be assumed.
    body = await request.json()
else:
    body = body_bytes

I can make a PR for this, but I would like to learn the maintainers' opinion on this.
@tiangolo @dmontagu @phy25, thanks for the great project!

@jbkoh jbkoh added the bug Something isn't working label Feb 22, 2020
@xeor
Copy link

xeor commented Mar 9, 2020

Looking forward to this. I have a bunch of raw text (words from files) I want pumping into my api with a simple api as possible..

jacob-vincent added a commit to jacob-vincent/fastapi that referenced this issue Mar 17, 2020
@jacob-vincent
Copy link

I have the fix suggested above implemented, and it works in the project that I'm currently developing, but I'm unable to figure out a way to get all of the tests to pass. For example, I'm getting a test failure on tests/test_tutorial/test_body/test_tutorial001.py during test_post_broken_body(). The value of response.status_code is 422, which is causing the assertion of assert response.status_code == expected_status to fail. Any suggestions, anyone?

@heitorPB
Copy link

heitorPB commented Apr 2, 2020

Oh, that's why I cant have a YAML body in an endpoint...

With @jbkoh's proposal, I get this error in the tests:

fastapi/routing.py:151: error: Incompatible types in assignment (expression has type "bytes", variable has type "Optional[FormData]")
Found 1 error in 1 file (checked 40 source files)

@jbkoh
Copy link
Author

jbkoh commented Apr 2, 2020

@heitorPB What's the input here?

@heitorPB
Copy link

heitorPB commented Apr 2, 2020

@jbkoh
This is the output of the scripts/tests.sh with the changes you suggested. I'm running on master branch.

Did you run the tests?

@jbkoh
Copy link
Author

jbkoh commented Apr 2, 2020

@heitorPB Ah, no I didn't. I was waiting for the maintainers' response before properly implementing the proposed solution. I will try to find some cycles to actually run the test. I guess @jacob-vincent had a similar issue.

@tiangolo
Copy link
Owner

If you are sending plain text files, the best way to do it (and the common practice / "standard") is to send them as form data.

Here are the docs for that: https://fastapi.tiangolo.com/tutorial/request-files/

Another option is to read the body directly as a stream by getting the request directly: https://fastapi.tiangolo.com/advanced/using-request-directly/

@tiangolo
Copy link
Owner

@jbkoh About the suggested change, I'm not sure yet how it would work and what else would need to interact with it, but maybe you can start a PR and we can see what else could break or what else would be needed for it.

@heitorPB
Copy link

Another option is to read the body directly as a stream by getting the request directly: https://fastapi.tiangolo.com/advanced/using-request-directly/

The downside is that it is not documented in the OpenAPI.json file.

@htapal
Copy link

htapal commented Apr 14, 2020

What's the point of the media_type option in Body if it won't be honored.

@phy25
Copy link

phy25 commented Apr 15, 2020

What's the point of the media_type option in Body if it won't be honored.

@htapal It's currently passed onto openapi generation.

@htapal
Copy link

htapal commented Apr 15, 2020

What's the point of the media_type option in Body if it won't be honored.

@htapal It's currently passed onto openapi generation.

What good does that do if the framework doesn't honor the media type of the body? Passing on to the openapi spec is a by product, cherry on top you could say for the functionality provided by the framework. In this case, setting the media_type of the Body correctly sets up the API docs with the interactive docs even customizing the form based on what the resource is declaring is the media type it accepts, however, when posting the data, the framework completely ignores the declare Body media type.

The workaround of using Form or the request directly is not acceptable. Fastapi is a framework to build REST apis and it's a very, very good one. But for it to only expert JSON and form-data as the request body is very short-sighted especially since the Body class allows you to declare the media type.

@heitorPB
Copy link

@jacob-vincent did you had any success to make the tests pass?

@AdmiralNemo
Copy link

Is there any other way to document the request body in OpenAPI documentation, besides Body(...)?

I have a legacy service that I am porting to FastAPI, and one of the operations accepts a YAML body. I am happy to parse it myself using the raw request, but I do not see any way to express that a) this operation accepts a body, or b) that the body should be application/yaml.

Most of the users of my service use the Swagger UI directly, so not being able to enter the body for the request there would be a huge problem for them.

@coryvirok
Copy link

Any update on the fix for this? I'd love to be able to get my API docs to include the request body documentation for text/plain POST endpoints. :)

@rdpravin1895
Copy link

Any update on this? This will be highly useful for media types like yaml, graphql, etc where the parsing can be done in the api implementation.

@laanak08
Copy link

laanak08 commented Apr 10, 2021

@rdpravin1895

Any update on this? This will be highly useful for media types like yaml, graphql, etc where the parsing can be done in the api implementation.

from fastapi import FastAPI, Request

app = FastAPI()


@app.post("/some/path/{arbitrary_url_param}")
async def arbitrary_endpoint(request: Request, arbitrary_url_param: str, arbitrary_qs_param:int):
    body: bytes = await request.body()

@rdpravin1895
Copy link

rdpravin1895 commented Apr 12, 2021

@laanak08 What I'm looking for is to provide the body as text/plain content-type in the apidocs ui so that I don't have to provide the input as url-encoded json-string always for normal text, yaml, graphql inputs. I believe if this issue is fixed, the functionality can be achieved.

@AdmiralNemo
Copy link

Right, the problem here is that there is no way to both handle a non-JSON body and display a body field in OpenAPI/Swagger UI. Right now it's either-or: the only way to have a body field show up in Swagger UI is to accept only JSON.

@mkiesel
Copy link

mkiesel commented Oct 12, 2021

(edit: it seems fa7e3c9 / #2118 added support for looking at the content type :) )

It'd be great if there was at least an option to make FastAPI parse the body as JSON only if the request body content type is actually application/json. (similar to the code mentioned here)

One major advantage of this would be that it also mitigates CSRF attacks since attackers cannot set the POST body content type to application/json but only to the three content types that represent simple requests.

@alex-kowalczyk
Copy link

alex-kowalczyk commented Feb 7, 2022

fa7e3c9 could be extended to first use body_field.field_info.media_type when provided, or go with current logic when unspecified. This would allow to force using proper content-type e.g. for callbacks/hooks where 'content-type' received is not under control of an endpoint's implementer.

@tiangolo tiangolo added question Question or problem reviewed and removed bug Something isn't working labels Feb 23, 2023
@tiangolo tiangolo changed the title [BUG] Body is always parsed as JSON regardless of media_type Body is always parsed as JSON regardless of media_type Feb 24, 2023
Repository owner locked and limited conversation to collaborators Feb 28, 2023
@tiangolo tiangolo converted this issue into discussion #9159 Feb 28, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests