Flask-Restful Error: request Content-Type was not 'application/json'."}

Question:

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())
Asked By: jadside

||

Answers:

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())
Answered By: Sai

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())
Answered By: Daedalus

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})
Answered By: McConnell

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

Answered By: azzamsa

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.

Answered By: zr0gravity7

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.

Answered By: cbueche

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

Answered By: none_nfg
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.