-1

Minimal example:

#!/usr/bin/env python3

import json
from dataclasses import dataclass

from flask import Flask, request, make_response
from flask_restful import Resource, Api


@dataclass
class Foo:
    bar: int


class WebController(Resource):
    def __init__(self, factor) -> None:
        self._factor = factor

    def post(self):
        foo = Foo(**json.loads(request.data))
        return make_response(str(self._factor * foo.bar), 200)


def main():
    app = Flask(__name__)
    api = Api(app)
    api.add_resource(WebController, "/json_post_endpoint", resource_class_kwargs={"factor": 2})
    app.run(port=8080)


if __name__ == "__main__":
    main()

Running it, then sending a curl without the header:

curl -X POST --url http://localhost:8080/json_post_endpoint --data '{ "bar": 42 }'
{"message": "Internal Server Error"}

Logs:

[2023-01-06 10:34:23,616] ERROR in app: Exception on /json_post_endpoint [POST]
Traceback (most recent call last):
  File "/home/tobias/.local/lib/python3.10/site-packages/flask/app.py", line 1820, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/tobias/.local/lib/python3.10/site-packages/flask/app.py", line 1796, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "/home/tobias/.local/lib/python3.10/site-packages/flask_restful/__init__.py", line 467, in wrapper
    resp = resource(*args, **kwargs)
  File "/home/tobias/.local/lib/python3.10/site-packages/flask/views.py", line 107, in view
    return current_app.ensure_sync(self.dispatch_request)(**kwargs)
  File "/home/tobias/.local/lib/python3.10/site-packages/flask_restful/__init__.py", line 582, in dispatch_request
    resp = meth(*args, **kwargs)
  File "/home/tobias/Documents/main.py", line 20, in post
    foo = Foo(**json.loads(request.data))
  File "/usr/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/usr/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

But with the header, the curl is fine:

curl -X POST --url http://localhost:8080/json_post_endpoint --header 'Content-Type: application/json' --data '{ "bar": 42 }'
84

What does adding the header to the request change for the server to behave differently?

Tobias Hermann
  • 9,936
  • 6
  • 61
  • 134
  • Oh, the answer is [here](https://stackoverflow.com/questions/10999990/get-raw-post-body-in-python-flask-regardless-of-content-type-header). Closing. – Tobias Hermann Jan 06 '23 at 09:49

1 Answers1

1

You are doing

json.loads(request.data)

observe that flask.request has method for that, namely get_json

Parse data as JSON.

If the mimetype does not indicate JSON (application/json, see is_json), or parsing fails, on_json_loading_failed() is called and its return value is used as the return value. By default this raises a 400 Bad Request error.

Parameters

  • force (bool) – Ignore the mimetype and always try to parse JSON.
  • silent (bool) – Silence mimetype and parsing errors, and return None instead.
  • cache (bool) – Store the parsed JSON to return for subsequent calls.

(...)

So if you desire to have your request parsed as JSON regardless of header, you should do

request.get_json(force=True)

if you do not wish to care about case when what you got is not legal JSON or

request.get_json(force=True,silent=True)

and then be ready to get None denoting you got illegal JSON.

Daweo
  • 31,313
  • 3
  • 12
  • 25