'Flask-restx request parser returns 400 Bad Request

I'm using flask-restx in my flask application but each time I use the swagger ui to make a request it returns this 400:

http://127.0.0.1:5000/api/user/register/?password=test&email=test&username=test
{
  "message": "Did not attempt to load JSON data because the request Content-Type was not 'application/json'."
}

My file structure:

flask-project/
├─ run.py
├─ app/
│  ├─ main/
│  │  ├─ __init__.py
│  │  ├─ api/
│  │  │  ├─ __init__.py
│  │  │  ├─ user.py
│  ├─ __init__.py

app/_init_.py

import os
from dotenv import load_dotenv

from flask import Flask

# Extensions
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_socketio import SocketIO

db = SQLAlchemy()
ma = Marshmallow()
socketio = SocketIO()

load_dotenv()


def create_app(debug=False):
    from app.main.api import api_blueprint

    app = Flask(__name__)
    app.debug = debug
    app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
    app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///database.sqlite3"
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "true"  # !! Only in development environment.

    db.init_app(app)  # Flask-SQLAlchemy must be initialized before Flask-Marshmallow.
    ma.init_app(app)
    socketio.init_app(app)

    app.register_blueprint(api_blueprint, url_prefix='/api')

    with app.app_context():
        db.create_all()

    @app.before_first_request
    def create_tables():
        """ Pre-populate specific tables """
        pass

    return app

user.py

from flask_restx import Namespace, Resource, reqparse

api = Namespace('user')

@api.route('/register')
@api.param('username', 'Username')
@api.param('email', 'Email')
@api.param('password', 'Password')
class CreateUser(Resource):
    def put(self):
        parser = reqparse.RequestParser()
        parser.add_argument('username', location='json', type=str)
        parser.add_argument('email', location='json', type=str)
        parser.add_argument('password', location='json', type=str)
        args = parser.parse_args()
        
        return args

When I placed print statements in the put method, I found that the method is called and anything before I define args will print. After the line where I define args nothing prints. What am I doing wrong?



Solution 1:[1]

I figured out that by downgrading from Werkzeug 2.1.2 to 2.0.2 it fixed the issue.

Solution 2:[2]

I just went through this on a project this weekend.

api.param is for parameters that are in the path, like /users/<user_id>. The parameters you are using are query parameters, not path parameters. So remove the 3 @api.param decorators.

As I understand it, location='json' is for extracting data from the body of a request sent in JSON. Your parameters are query parameters. Change your parser.add_argument calls to use location='args'.Then after calling parse_args() as you have in your posted code, you should be able to do args['username'] to get the value of the username query arg, which will give None if username arg was not given.

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 Peter
Solution 2 PaulMcG