【问题标题】:FLASK REST API returns 400 on POSTFLASK REST API 在 POST 上返回 400
【发布时间】:2020-04-01 23:03:11
【问题描述】:

我正在为一个简单的 Todo 应用程序构建一个 REST API,使用 flask 和 SQLAlchemy 作为我的 ORM。我正在使用 Postman 测试我的 API。我在 Windows 10 64 位机器上。

GET 请求有效并返回我使用 python 输入数据库的数据。

我现在想尝试添加一个任务。但是当我发布我的请求时,我收到一个错误。

我在烧瓶中的路线如下所示。

#add task
@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
    if not request.json or not 'title' in request.json:
        raise InvalidUsage('Not a valid task!', status_code=400)
    task = {
        'title': request.json['title'],
        'description': request.json['description'],
        'done': False
    }
    Todo.add_todo(task)
    return jsonify({'task': task}), 201

它在 Todo 对象上调用的方法看起来像这样。

def add_todo(_title, _description):
    new_todo = Todo(title=_title, description=_description , completed = 0)
    db.session.add(new_todo)
    db.session.commit()

我的尝试

我认为可能是我的 Postman Params 中的 ' 引起了问题,所以我删除了它们。但我仍然得到同样的错误。

然后我认为 Postman 发送 POST 的方式可能不正确,因此我检查以确保 Content-Type 标头正确。设置为application/json

最后,为了确认问题是烧瓶不喜欢请求,我删除了添加任务路由中的检查以确保请求具有标题。所以它看起来像这样。

if not request.json:

我得到同样的错误。所以我认为问题一定出在我实际发送 POST 的方式上,而不是某种格式问题。

我的整个代码如下所示。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import json
from flask import jsonify
from flask import request

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///todo.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

class Todo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(300), unique=False, nullable=False)
    description = db.Column(db.String(), unique=False, nullable=False)
    completed = db.Column(db.Boolean, nullable=False)

    def json(self):
        return {'id': self.id,'title': self.title, 'description': self.description, 'completed': self.completed}

    def add_todo(_title, _description):
        new_todo = Todo(title=_title, description=_description , completed = 0)
        db.session.add(new_todo)
        db.session.commit()

    def get_all_tasks():
        return [Todo.json(todo) for todo in Todo.query.all()]

    def get_task(_id):
        task = Todo.query.filter_by(id=_id).first()
        if task is not None:
            return Todo.json(task)
        else:
            raise InvalidUsage('No task found', status_code=400)

    def __repr__(self):
        return f"Todo('{self.title}')"




class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv

@app.route('/')
def hello_world():
    return 'Hello to the World of Flask!'

#get all tasks
@app.route('/todo/api/v1.0/tasks', methods=['GET'])
def get_tasks():
    return_value = Todo.get_all_tasks()
    return jsonify({'tasks': return_value})

#get specific task
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['GET'])
def get_task(task_id):
    task = Todo.get_task(task_id)
    #if len(task) == 0:
        #raise InvalidUsage('No such task', status_code=404)
    return jsonify({'task': task})

#add task
@app.route('/todo/api/v1.0/tasks', methods=['POST'])
def create_task():
    if not request.json or not 'title' in request.json:
        raise InvalidUsage('Not a valid task!', status_code=400)
    task = {
        'title': request.json['title'],
        'description': request.json['description'],
        'done': False
    }
    Todo.add_todo(task)
    return jsonify({'task': task}), 201

#update task
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['PUT'])
def update_task(task_id):
    task = [task for task in tasks if task['id'] == task_id]
    if len(task) == 0:
        raise InvalidUsage('No provided updated', status_code=400)
    if not request.json:
        raise InvalidUsage('request not valid json', status_code=400)
    if 'title' in request.json and type(request.json['title']) != unicode:
        raise InvalidUsage('title not unicode', status_code=400)
    if 'description' in request.json and type(request.json['description']) != unicode:
        raise InvalidUsage('description not unicode', status_code=400)
    if 'done' in request.json and type(request.json['done']) is not bool:
        raise InvalidUsage('done not boolean', status_code=400)
    task[0]['title'] = request.json.get('title', task[0]['title'])
    task[0]['description'] = request.json.get('description', task[0]['description'])
    task[0]['done'] = request.json.get('done', task[0]['done'])
    return jsonify({'task': task[0]})

#delete task
@app.route('/todo/api/v1.0/tasks/<int:task_id>', methods=['DELETE'])
def delete_task(task_id):
    task = [task for task in tasks if task['id'] == task_id]
    if len(task) == 0:
        raise InvalidUsage('No task to delete', status_code=400)
    tasks.remove(task[0])
    return jsonify({'result': True})


@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response

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

编辑:

原来我没有正确设置 POSTMAN 中的请求类型。我已将其更新为标题中的“应用程序/json”。现在我收到一个不同的错误。

错误请求无法解码 JSON 对象:预期值:第 1 行 第 1 列(字符 0)

我已经像以前一样尝试了前面的所有步骤,但我仍然收到此错误。

编辑 2:

根据下面的回复,我尝试将值放入 POST 的正文中。但我仍然收到 400 回复。

【问题讨论】:

  • 您的 JSON 是一个两级结构,而您正在从顶层读取所有数据。

标签: python rest flask postman


【解决方案1】:

从图像 [第二个邮递员屏幕截图] 看来,您在 查询字符串 中传递数据,但 create_task() 期望它们在 请求正文 中。

要么将所有出现的request.json 替换为create_task() 中的request.args(使其与查询参数一起使用),要么保持原样并在请求正文中发送数据。

curl -X POST http://localhost:5000/todo/api/v1.0/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Learn more flask","description":"its supper fun"}'

另外,看看Get the data received in a Flask request


已编辑

将您的 add_todo 更新为类似

@classmethod
def add_todo(cls, task):
    new_todo = cls(title=task["title"], description=task["description"], completed=0)
    db.session.add(new_todo)
    db.session.commit()

相关:generalised insert into sqlalchemy using dictionary.

【讨论】:

  • 我意识到我没有按照您的链接正确格式化原始请求。我更新了它。现在我收到以下错误。类型错误:add_todo() 缺少 1 个必需的位置参数:'_description'
  • 这是一个不同的问题。这是因为定义是 add_todo(_title, _description): 但你用 Todo.add_todo(task) 调用它。
  • 对,但任务对象中有描述。它是在第 80 行创建的。
  • 这成功了!谢谢你。不过,我不太确定 cls 是怎么回事。
  • cls 是调用该方法的类,在这种情况下是您的 Todo 模型。更多详情请查看stackabuse.com/pythons-classmethod-and-staticmethod-explained
猜你喜欢
  • 2021-01-13
  • 2021-06-22
  • 2017-08-13
  • 1970-01-01
  • 2017-11-30
  • 2019-12-31
  • 1970-01-01
  • 2022-08-03
  • 2020-02-03
相关资源
最近更新 更多