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())
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())
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())
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})
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
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.
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.
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
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())
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())
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())
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})
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
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.
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.
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