烧瓶应用路由返回"Bad Request"错误



我正在Flask中学习完整堆栈,并且在API的特定路由上遇到了问题。正在开发的API是一个书籍列表,特别是我正在尝试获取特定书籍的数据,比如ID=8的书籍。URI是http://127.0.0.1:5000/books/8.但是,这会返回一个400错误(错误请求(。

我真的看不出哪里出了问题。我已经用GET和PATCH方法定义了路由"/books/int:book_id",所以我希望该路由能够工作。当我用curl测试路线时,我也会看到错误,例如:

curl -X PATCH -H "Content-Type: application/json" -d '{"rating":"1"}' http://127.0.0.1:5000/books/8

有关特定路线,请参见下文:

@app.route('/books/<int:book_id>', methods=['GET', 'PATCH'])
def update_book_rating(book_id):

body = request.get_json() 
try:
book = Book.query.filter_by(Book.id==book_id).one_or_none()
if book is None:
abort(404)

if 'rating' in body:
book.rating = int(body.get('rating'))
book.update() #Class book in models.py has an update method which executes a commit()
return jsonify({
'success': True,
'id': book.id
})

except Exception as e:
print(e)
abort(400)

如果有帮助的话,我还会添加完整的代码。请注意,Book对象是在一个单独的文件中定义的,我不会把它放在这里。

import os
from flask import Flask, request, abort, jsonify
from flask_sqlalchemy import SQLAlchemy  # , or_
from flask_cors import CORS
import random
from models import setup_db, Book
BOOKS_PER_SHELF = 8
# @TODO: General Instructions
#   - As you're creating endpoints, define them and then search for 'TODO' within the frontend to update the endpoints there.
#     If you do not update the endpoints, the lab will not work - of no fault of your API code!
#   - Make sure for each route that you're thinking through when to abort and with which kind of error
#   - If you change any of the response body keys, make sure you update the frontend to correspond.
def paginate_books(request, selection):
page = request.args.get('page', 1, type=int)
start = (page - 1) * BOOKS_PER_SHELF
end = start + BOOKS_PER_SHELF
books = [book.format() for book in selection]
current_books = books[start:end]
return current_books

def create_app(test_config=None):
# create and configure the app
app = Flask(__name__)
setup_db(app)
CORS(app)
# CORS Headers
@app.after_request
def after_request(response):
response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization,true")
response.headers.add("Access-Control-Allow-Methods", "GET,PUT,PATCH,POST,DELETE,OPTIONS")
return response
# @TODO: Write a route that retrivies all books, paginated.
#         You can use the constant above to paginate by eight books.
#         If you decide to change the number of books per page,
#         update the frontend to handle additional books in the styling and pagination
#         Response body keys: 'success', 'books' and 'total_books'
# TEST: When completed, the webpage will display books including title, author, and rating shown as stars
@app.route('/books', methods=['GET'])
def get_books():

selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
if len(current_books) == 0:
abort(404)
return jsonify({
'success': True,
'books': current_books,
'total_books': len(Book.query.all())
})
# @TODO: Write a route that will update a single book's rating.
#         It should only be able to update the rating, not the entire representation
#         and should follow API design principles regarding method and route.
#         Response body keys: 'success'
# TEST: When completed, you will be able to click on stars to update a book's rating and it will persist after refresh
@app.route('/books/<int:book_id>', methods=['GET', 'PATCH'])
def update_book_rating(book_id):

body = request.get_json() 
try:
book = Book.query.filter_by(Book.id==book_id).one_or_none()
if book is None:
abort(404)

if 'rating' in body:
book.rating = int(body.get('rating')) 
book.update() #Class book in models.py has an update method which executes a commit()
return jsonify({
'success': True,
'id': book.id
})

except Exception as e:
print(e)
abort(400)

# @TODO: Write a route that will delete a single book.
#        Response body keys: 'success', 'deleted'(id of deleted book), 'books' and 'total_books'
#        Response body keys: 'success', 'books' and 'total_books'
@app.route('/delete/<int:book_id>', methods=['DELETE'])
def delete_book(book_id):
try:
book = Book.query.filter_by(Book.id==book_id).one_or_none()
if book is None:
abort(404)
book.delete()
selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
return jsonify({
'success': True,
'deleted': book_id,
'books': current_books,
'total_books': len(Book.query.all())
})
except:
abort(422)

# TEST: When completed, you will be able to delete a single book by clicking on the trashcan.
# @TODO: Write a route that create a new book.
#        Response body keys: 'success', 'created'(id of created book), 'books' and 'total_books'
# TEST: When completed, you will be able to a new book using the form. Try doing so from the last page of books.
#       Your new book should show up immediately after you submit it at the end of the page.
@app.route('/books', methods=['POST'])
def create_book():
body = request.get_json()
new_title = body.get('title', None)
new_author = body.get('author', None)
new_rating = body.get('rating', None)
try:
book = Book(title=new_title, author=new_author, rating=new_rating)
book.insert()
selection = Book.query.order_by(Book.id).all()
current_books = paginate_books(request, selection)
return jsonify({
'success': True,
'created': book.id,
'books': current_books,
'total_books': len(Book.query.all())
})
except:
abort(422)
@app.errorhandler(400)
def bad_request(error):
return jsonify({
'success': False,
'error': 400,
'message': 'Server cannot or will not process the request due to client error (for example, malformed request syntax, invalid request message framing, or deceptive request routing).'
}), 400

@app.errorhandler(404)
def not_found(error):
return jsonify({
'success': False,
'error': 404,
'message': 'resource not found'
}), 404
@app.errorhandler(405)
def not_found(error):
return jsonify({
'success': False,
'error': 405,
'message': 'method not allowed'
}), 405
@app.errorhandler(422)
def unprocessable(error):
return jsonify({
'success': False,
'error': 422,
'message': 'unprocessable'
}), 422
return app

您确定要在表单中添加CSRF令牌吗?如果没有,可以抛出这个错误。您可以通过深入开发人员工具Network中的请求详细信息来查看这是否是错误-->预览

如果问题是CSRF,请参阅此处,以获得使用烧瓶添加CSRF保护的良好指南https://testdriven.io/blog/csrf-flask/

相关内容