7

I was following this tutorial and it was going pretty well. He then introduced reqparse and I followed along. I tried to test my code and I get this error {'message': "Did not attempt to load JSON data because the request Content-Type was not 'application/json'."}

I don't know if I'm missing something super obvious but I'm pretty sure I copied his code exactly. here's the code: main.py

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

app = Flask(__name__)
api = Api(app)

#basic get and post
names = {"sai": {"age": 19, "gender": "male"},
            "bill": {"age": 23, "gender": "male"}}
class HelloWorld(Resource):
    def get(self, name, numb):
        return names[name]

    def post(self):
        return {"data": "Posted"}

api.add_resource(HelloWorld, "/helloworld/<string:name>/<int:numb>")

# getting larger data
pictures = {}
class picture(Resource):
    def get(self, picture_id):
        return pictures[picture_id]

    def put(self, picture_id):
        print(request.form['likes'])
        pass

api.add_resource(picture, "/picture/<int:picture_id>")

# reqparse
video_put_args = reqparse.RequestParser() # make new request parser object to make sure it fits the correct guidelines
video_put_args.add_argument("name", type=str, help="Name of the video")
video_put_args.add_argument("views", type=int, help="Views on the video")
video_put_args.add_argument("likes", type=int, help="Likes on the video")

videos = {}

class Video(Resource):
    def get(self, video_id):
        return videos[video_id]

    def post(self, video_id):
        args = video_put_args.parse_args()
        print(request.form['likes'])
        return {video_id: args}

api.add_resource(Video, "/video/<int:video_id>")

if __name__ == "__main__":
    app.run(debug=True)

test_rest.py

import requests

BASE = "http://127.0.0.1:5000/"

response = requests.post(BASE + 'video/1', {"likes": 10})

print(response.json())
Sunderam Dubey
  • 1
  • 11
  • 20
  • 40
jadside
  • 135
  • 1
  • 1
  • 5
  • I am having the EXACT same problem and I believe that it was because `flask_restful ` updated something and the syntax changed. Do you remember what exactly you used instead of using `parse_args()`? – Raul Chiarella May 10 '23 at 23:25

8 Answers8

5

If you are able to change the code:

I manage to solve the issue by adding location=<target> to parser.add_argument() function.

  parser.add_argument("email", type=str, required=True)
+ parser.add_argument("email", type=str, required=True, location='form')

You need to add the correct location of your input data. Some possible values are json, args, and form. Learn more at: https://flask-restful.readthedocs.io/en/latest/api.html#reqparse.RequestParser.parse_args

In my case, it is form. because I use multipart/form-data as input.

If you are unable to change the code:

Downgrade the werkzeug to the version before this commit

Credit: Flask-restx request parser returns 400 Bad Request

azzamsa
  • 1,805
  • 2
  • 20
  • 28
  • Right; from an HTML form that made a `post` request to the route that was meant to handle the form, I had to change from using `request.json["key"]` to `request.form.get("key")`. I think this broke for me after an update to Python 3.10, but not sure. – louisdeb Jun 05 '23 at 12:47
4

Currently following the same tutorial and faced the same issue. Solved mine by adding the keyword argument json for the data

response = requests.post(BASE + 'video/1', json={"likes": 10})
McConnell
  • 171
  • 1
  • 3
3

I don't know why you have an issue as far as I can tell you did copy him exactly how he did it. Here's a fix that'll work although I can't explain why his code works and yours doesn't. His video is two years old so it could be deprecated behaviour.

import requests
import json

BASE = "http://127.0.0.1:5000/"

payload = {"likes": 10}

headers = {'accept': 'application/json'}
response = requests.post(BASE + 'video/1', json=payload)

print(response.json())
Ankit Tiwari
  • 4,438
  • 4
  • 14
  • 41
Sai
  • 166
  • 6
2

azzamsa is correct. I could add the following : if your API worked and suddenly stopped after a module update with an error like something along Did not attempt to load JSON data because the request Content-Type was not 'application/json', maybe you are using GET with parameters, eg for me curl "http://localhost:5000/api/mac?mac=3c:52:82:17:2e:e8". My code was

parser = reqparse.RequestParser()
parser.add_argument("mac", type=str)
args = parser.parse_args()

After changing it to

parser = reqparse.RequestParser()
parser.add_argument("mac", type=str ,location='args')
args = parser.parse_args()

I got the previous behaviour and could read args['mac'] after this fix.

cbueche
  • 51
  • 2
1

You can set the header like the error message says.

import requests, json

BASE = "http://127.0.0.1:5000/"
# Set request's header.
headers = {"Content-Type": "application/json; charset=utf-8"}
# Set data.
data = {"likes": 10}
# 
response = requests.post(BASE + 'video/1', headers=headers, json=data)

print("Status Code: ", response.status_code)
print("JSON Response: ", response.json())
Daedalus
  • 295
  • 2
  • 17
1

You can use the Response.get_json() method instead of the json property, which lets you specify a force parameter:

force (bool) — Ignore the mimetype and always try to parse JSON

Usage would then be:

import requests

response = requests.post('http://127.0.0.1:5000/video/1', {'likes': 10})

response_data_forced_json = response.get_json(force=True)

This is actually what is called when getting the Response.json property, only with the default arguments.

zr0gravity7
  • 2,917
  • 1
  • 12
  • 33
0

I've faced with similar trouble. I've got error about content type, but in flask-restx. My conclusion is to use reqparse to define required parameters, and also for getting this parameters, other way you'll get the same error.

I've used reqparse to define file_uploader, and api.payload, to get other data

upload_parser = api.parser()
upload_parser.add_argument('file', location='files',
                           type=FileStorage, required=True)

hostauth_create_fields = api.model(
    'HostAuthCreate', {
        'name': fields.String(description="The name of the instance", required=True),
        'username': fields.String(description="Username to connect", required=True),
        'password': fields.String(description="Password to connect", required=False)
    }
)

@api.route('/api/hostauth')
class HostAuthView(Resource):
    @api.expect(upload_parser, hostauth_create_fields)
    def post(self):
        args = upload_parser.parse_args()
        args.get('file')
        api.payload.get('name') # This line will cause a error
        return {'name': args.get('name')}, 201

But that's possible to parse all args via upload_parser:

upload_parser = api.parser()
upload_parser.add_argument('file', location='files',
                           type=FileStorage, required=True)
upload_parser.add_argument('name', type=str, required=True, location="form")
upload_parser.add_argument('username', type=str, required=True, location="form")
upload_parser.add_argument('password', type=str, location="form")

And to get other params, use:

args.get('name')

Possibly api.model rewrites header that responsible for accepted content type set by reqparse. So you should choose reqparse if you have files as args. In other routes you can use api.model again

none_nfg
  • 21
  • 1
0

pip install -r requirements.txt

file requirements.txt such as:

  • Flask==1.1.2 Flask-Cors==3.0.8
  • Flask-RESTful==0.3.8
  • Jinja2==2.11.2
  • MarkupSafe==1.1.1
  • itsdangerous==1.1.0
  • Werkzeug==1.0.1

-> I think this error was Flask version > 2.0. If you use Flask version > 2.0 you add location ='form':

ex: parse.add_argument('ID', location='form')

BKPHAN
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 06 '23 at 23:19