'Flask: The browser (or proxy) sent a request that this server could not understand

I created a small Flask service. However each time I tried to use the say-hi endpoint, I get the following message:

{
    "message": "The browser (or proxy) sent a request that this server could not understand."
}

My Flask service looks like this:

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

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

class HelloResource(Resource):
    def get(self):
        return { 'message': 'Hello' }

class SayHiResource(Resource):
    def get(self):
        parser = reqparse.RequestParser()
        parser.add_argument('name', required=True, help='Name cannot be blank')
        args = parser.parse_args()

        return { 'message': 'Hello ' + args['name'] }

api.add_resource(HelloResource, '/hello')
api.add_resource(SayHiResource, '/say-hi')

if __name__ == '__main__':
    app.run(debug=True, load_dotenv=True)

However, there is not much information about why is failing.

The way I'm running is by using gunicorn and the serviceEntrypoint.py file, which only has this content:

from src.api.service import app

if __name__ == '__main__':
    app.run()

Here is my folder structure

.
├── requirements.txt
├── serviceEntrypoint.py
└── src
    ├── __init__.py
    └── api
        ├── __init__.py
        └── service.py

Why the /hello ending works, but the say-hi doesn't when I call to http://localhost:8000/say-hi?name=John?



Solution 1:[1]

if you run the flask debug server and issue your HTTP request http://localhost:8000/say-hi?name=John, you will see that the actual error is:

message "Did not attempt to load JSON data because the request Content-Type was not 'application/json'."

There is documentation and examples here, but it boils down to choosing if the request should be a GET or a POST. The way you structured your request - you are passing 1 field only, the username - it looks like a GET, in this case you should have:

api.add_resource(SayHiResource, '/say-hi/<username>')

and class:

class SayHiResource(Resource):
    def get(self, username):
        return { 'message': 'Hello ' + username }

if you want to implement a POST request, please trace in the documentation the example that is triggered by the call: curl http://localhost:5000/todos -d "task=something new" -X POST -v

Update:

for using query parameters, you can use the request.args:

api.add_resource(SayHiResource, '/say-hi')

class:

class SayHiResource(Resource):
    def get(self):
        username = request.args.get("username")
        status = request.args.get("status")
        # print(query_args)
        return { 'message': 'Hello {}, your status is: {}'.format(username, status) }

example:

[http_offline@greenhat-35 /tmp/] > curl 'http://localhost:8000/say-hi?username=lala&status=enabled'
{
    "message": "Hello lala, your status is: enabled"
}
[http_offline@greenhat-35 /tmp/] > 

Solution 2:[2]

The solution to this is to add location='args', so that reqparse uses only the query string value:

parser.add_argument('name', required=True, help='Name cannot be blank', location='args')

The reason for the issue is that Argument has 'json' as the default location and recent versions of Werkzeug (used by flask) will raise an exception when reqparse tries to read json data from the (non-json) request. This should probably be considered a bug in reqparse, but it's deprecated, so don't count on an update.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2