Flask вернуть ошибку

I have created a simple flask app that and I’m reading the response from python as:

response = requests.post(url,data=json.dumps(data), headers=headers ) 
data = json.loads(response.text)

Now my issue is that under certain conditions I want to return a 400 or 500 message response. So far I’m doing it like this:

abort(400, 'Record not found') 
#or 
abort(500, 'Some error...') 

This does print the message on the terminal:

enter image description here

But in the API response I kept getting a 500 error response:

enter image description here

The structure of the code is as follows:

|--my_app
   |--server.py
   |--main.py
   |--swagger.yml

Where server.py has this code:

from flask import render_template
import connexion
# Create the application instance
app = connexion.App(__name__, specification_dir="./")
# read the swagger.yml file to configure the endpoints
app.add_api("swagger.yml")
# Create a URL route in our application for "/"
@app.route("/")
def home():
    """
    This function just responds to the browser URL
    localhost:5000/

    :return:        the rendered template "home.html"
    """
    return render_template("home.html")
if __name__ == "__main__":
    app.run(host="0.0.0.0", port="33")

And main.py has all the function I’m using for the API endpoints.

E.G:

def my_funct():
   abort(400, 'Record not found') 

When my_funct is called, I get the Record not found printed on the terminal, but not in the response from the API itself, where I always get the 500 message error.

Ответ сервера

Flask предлагает три варианта для создания ответа:

  1. В виде строки или с помощью шаблонизатора
  2. Объекта ответа
  3. Кортежа в формате (response, status, headers) или (response, headers)

Далее о каждом поподробнее.

Создание ответа в виде строки

@app.route('/books/<genre>')
def books(genre):
    return "All Books in {} category".format(genre)

До сих пор этот способ использовался, чтобы отправлять ответ клиенту. Когда Flask видит, что из функции представления возвращается строка, он автоматически конвертирует ее в объект ответа (с помощью метода make_response()) со строкой, содержащей тело ответа, статус-код HTTP 200 и значение text/html в заголовке content-type. В большинстве случаев это — все, что нужно. Но иногда необходимы дополнительные заголовки перед отправлением ответа клиенту. Для этого создавать ответ нужно с помощью функции make_response().

Создание ответа с помощью make_response()

Синтаксис make_response() следующий:

res_obj = make_response(res_body, status_code=200)

res_body — обязательный аргумент, представляющий собой тело ответа, а status_code — опциональный, по умолчанию его значение равно 200.

Следующий код показывает, как добавить дополнительные заголовки с помощью функции make_response().

from flask import Flask, make_response,

@app.route('/books/<genre>')
def books(genre):
    res = make_response("All Books in {} category".format(genre))
    res.headers['Content-Type'] = 'text/plain'
    res.headers['Server'] = 'Foobar'
    return res

Следующий — демонстрирует, как вернуть ошибку 404 с помощью make_response().

@app.route('/')
def http_404_handler():
    return make_response("<h2>404 Error</h2>", 400)

Настройка куки — еще одна базовая задача для любого веб-приложения. Функция make_response() максимально ее упрощает. Следующий код устанавливает два куки в клиентском браузере.

@app.route('/set-cookie')
def set_cookie():
    res = make_response("Cookie setter")
    res.set_cookie("favorite-color", "skyblue")
    res.set_cookie("favorite-font", "sans-serif")
    return res

Примечание: куки обсуждаются подробно в уроке «Куки во Flask».

Куки, заданные в вышеуказанном коде, будут активны до конца сессии в браузере. Можно указать собственную дату истечения их срока, передав в качестве третьего аргумента в методе set_cookie() количество секунд. Например:

@app.route('/set-cookie')
def set_cookie():
    res = make_response("Cookie setter")
    res.set_cookie("favorite-color", "skyblue", 60*60*24*15)
    res.set_cookie("favorite-font", "sans-serif", 60*60*24*15)
    return res

Здесь, у куки будут храниться 15 дней.

Создание ответов с помощью кортежей

Последний способ создать ответ — использовать кортежи в одном из следующих форматов:

(response, status, headers)

(response, headers)

(response, status)

response — строка, представляющая собой тело ответа, status — код состояния HTTP, который может быть указан в виде целого числа или строки, а headers — словарь со значениями заголовков.

@app.route('/')
def http_500_handler():
    return ("<h2>500 Error</h2>", 500)

Функция представления вернет ошибку HTTP 500 Internal Server Error. Поскольку при создании кортежей можно не писать скобки, вышеуказанный код можно переписать следующим образом:

@app.route('/')
def http_500_handler():
    return "<h2>500 Error</h2>", 500

Следующий код демонстрирует, как указать заголовки с помощью кортежей:

@app.route('/')
def render_markdown():
    return "## Heading", 200, {'Content-Type': 'text/markdown'}

Сможете догадаться, что делает следующая функция?

@app.route('/transfer')
def transfer():
    return "", 302, {'location': 'https://localhost:5000/login'}

Функция представления перенаправляет пользователя на https://localhost:5000/login с помощью ответа 302 (временное перенаправление). Перенаправление пользователей — настолько распространенная практика, что во Flask для этого есть даже отдельная функция redirect().

from flask import Flask, redirect

@app.route('/transfer')
def transfer():
    return redirect("https://localhost:5000/login")

По умолчанию, redirect() осуществляет 302 редиректы. Чтобы использовать 301, нужно указать это в функции redirect():

from flask import Flask, redirect

@app.route('/transfer')
def transfer():
    return redirect("https://localhost:5000/login", code=301)

Перехват запросов

В веб-приложениях часто нужно исполнить определенный код до или после запроса. Например, нужно вывести весь список IP-адресов пользователей, которые используют приложение или авторизовать пользователя до того как показывать ему скрытые страницы. Вместе того чтобы копировать один и тот же код внутри каждой функции представления, Flask предлагает следующие декораторы:

  • before_first_request: этот декоратор выполняет функцию еще до обработки первого запроса
  • before_request: выполняет функцию до обработки запроса
  • after_request: выполняет функцию после обработки запроса. Такая функция не будет вызвана при возникновении исключений в обработчике запросов. Она должна принять объект ответа и вернуть тот же или новый ответ.
  • teardown_request: этот декоратор похож на after_request. Но вызванная функция всегда будет выполняться вне зависимости от того, возвращает ли обработчик исключение или нет.

Стоит отметить, что если функция в before_request возвращает ответ, тогда обработчик запросов не вызывается.

Следующий код демонстрирует, как использовать эти точки перехвата во Flask. Нужно создать новый файл hooks.py с таким кодом:

from flask import Flask, request, g

app = Flask(__name__)

@app.before_first_request
def before_first_request():
    print("before_first_request() called")

@app.before_request
def before_request():
    print("before_request() called")

@app.after_request
def after_request(response):
    print("after_request() called")
    return response

@app.route("/")
def index():
    print("index() called")
    return '<p>Testings Request Hooks</p>'

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

После этого необходимо запустить сервер и сделать первый запрос, перейдя на страницу https://localhost:5000/. В консоли, где был запущен сервер, должен появиться следующий вывод:

before_first_request() called
before_request() called
index() called
after_request() called

Примечание: записи о запросах к серверу опущены для краткости.

После перезагрузки страницы отобразится следующий вывод.

before_request() called
index() called
after_request() called

Поскольку это второй запрос, функция before_first_request() не будет вызываться.

Отмена запроса с помощью abort()

Flask предлагает функцию abort() для отмены запроса с конкретным типом ошибки: 404, 500 и так далее. Например:

from  flask import  Flask,  abort

@app.route('/')
def index():
    abort(404)
    # код после выполнения abort() не выполняется

Эта функция представления вернет стандартную страницу ошибки 404, которая выглядит вот так:
функция вернет страницу ошибки 404

abort() покажет похожие страницы для других типов ошибок. Если нужно изменить внешний вид страниц с ошибками, необходимо использовать декоратор errorhandler.

Изменение страниц ошибок

Декоратор errorhandler используется для создания пользовательских страниц с ошибками. Он принимает один аргумент — ошибку HTTP, — для которой создается страница. Откроем файл hooks.py для создания кастомных страниц ошибок 404 и 500 с помощью декоратора:

from flask import Flask, request, g, abort
#...
#...
@app.after_request
def after_request(response):
    print("after_request() called")
    return response

@app.errorhandler(404)
def http_404_handler(error):
    return "<p>HTTP 404 Error Encountered</p>", 404

@app.errorhandler(500)
def http_500_handler(error):
    return "<p>HTTP 500 Error Encountered</p>", 500

@app.route("/")
def index():
    # print("index() called")
    # return '<p>Testings Request Hooks</p>'
    abort(404)

if  __name__  ==  "__main__":
#...

Стоит отметить, что оба обработчика ошибок принимают один аргумент error, который содержит дополнительную информацию о типе ошибки.

Если сейчас посетить корневой URL, отобразится следующий ответ:
кастомная страница ошибки 404

I am designing a RESTful API using Python and Flask. As expected, the API needs to receive an API request and return data if all goes well, but in the instance of an error, it needs to fail softly and return the proper error. I typically raise exceptions when an error results, but in this case I need to return the error message to the user (try-catch block?).

The way I am currently dealing with errors is to have my functions return both the data and an error, and to check for the data at each level, finally returning either the data or the error to the caller of the API function.

The problem with this is that it can get cumbersome when there are several levels of function calls, requiring my functions to pass data and errors several times and perform checks each time.

Is there a better way to do this? What are some improvements I could make to make the error propagation more simple and elegant?

Here is an example of the way I’m currently returning errors:

def get_data()
    data1, error = get_some_data() # function not shown
    if data1 is None:
         return None, "could not retrieve data1"
    data2, error = get_some_other_data() # function not shown
    if data2 is None:
         return None, "could not retrieve data2"
    return (data1, data2), None

@app.route("/api/method", methods=['GET'])
def method():
    data, error = get_data()
    if data is None:
        if error is None:
            error = "unknown error"
        return json.dumps({ "error": error }), 500
    return json.dumps({ "data": data }), 200

Free System Design Interview Course

Many candidates are rejected or down-leveled due to poor performance in their System Design Interview. Stand out in System Design Interviews and get hired in 2023 with this popular free course.

What is Flask?

Flask is a lightweight Python framework for quick and rapid web application development.

What are status codes in HTTP?

HTTP response status codes are used in web development to determine whether a particular HTTP request has been completed successfully.

Status code in a tuple

The view function can return the status code as one of a tuple’s elements. A response object is automatically created from the return result of a view function. It is possible to get a tuple back that contains additional information. The tuple with the status code to be returned can be in any of the following formats:

  1. (response, status_code)
  2. (response, status_code, headers)

Code

from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/userDetails", methods=["GET", "POST"])
def user_details():

    if request.method == "POST":

        username = request.form.get("username")
        firstname = request.form.get("firstname")
        lastname = request.form["lastname"]
        
        return "Success", 201

@app.route("/userSignUp", methods=["POST"])
def sign_up():

    if request.method == "POST":

        username = request.form.get("username")
        password = request.form.get("password")
        
        return "Success", 200, {"Access-Control-Allow-Origin": "*"}

Explanation

In the code above, the important lines to focus on are the following:

  • Line 14: We are returning the response as a tuple with the response body as Success and the status code of 201.
  • Line 24: We are returning the response as a tuple with the response body as Success, status code of 200, and some headers as a dictionary.

Use the following curl commands to test the userDetails endpoint.

curl -X POST http://localhost:5000/userDetails -H "Content-Type: application/x-www-form-urlencoded"  -d "username=sam&firstname=john&lastname=king"

Use the following curl commands to test the userSignUp endpoint.

curl -X POST http://localhost:5000/userSignUp -H "Content-Type: application/x-www-form-urlencoded"  -d "username=sam&firstname=john"

The make_response() method

The make_response method from Flask returns a Response object that can be used to send custom headers and change the properties such as status_code, mimetype, and so on.

We can set the status code using the make_response method in two ways:

  1. Pass the status code as a constructor parameter.
  2. Use the property status_code to set the status code. Here, the value set has to be an integer.

Code

from flask import Flask, make_response, request

app = Flask(__name__)

@app.route("/userDetails", methods=["GET", "POST"])
def user_details():

    if request.method == "POST":
        
        username = request.form.get("username")
        firstname = request.form.get("firstname")
        lastname = request.form["lastname"]

        response = make_response("<h1>Success</h1>", 201)
        return response

@app.route("/userSignUp", methods=["POST"])
def sign_up():

    if request.method == "POST":

        username = request.form.get("username")
        password = request.form.get("password")
        
        response = make_response("<h1>Success</h1>")
        response.status_code = 200
        return response

Explanation

In the code above, the important lines to focus on are the following:

  • Line 14: We use the make_response method to create an instance of the Response class. We pass the status code as a constructor parameter.
  • Lines 25–26: We use the make_response method to create an instance of the Response class. We set the status code by explicitly setting the status_code property of the response object.

Use the following curl commands to test the userDetails endpoint.

curl -X POST http://localhost:5000/userDetails -H "Content-Type: application/x-www-form-urlencoded"  -d "username=sam&firstname=john&lastname=king"

Use the following curl commands to test the userSignUp endpoint.

curl -X POST http://localhost:5000/userSignUp -H "Content-Type: application/x-www-form-urlencoded"  -d "username=sam&firstname=john"

CONTRIBUTOR

Abhilash

Copyright ©2023 Educative, Inc. All rights reserved

HTTP status code 400 (Bad Request) indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing). When building a web application using Flask, you may need to return a 400 Bad Request error to the client in certain circumstances. Here are several methods you can use to achieve this:

Method 1: Raising a HTTPException

To return a 400 (Bad Request) response on Flask using «Raising a HTTPException», you can use the abort function provided by Flask.

Here’s an example code snippet that raises a 400 error with a custom message:

from flask import Flask, abort

app = Flask(__name__)

@app.route('/example', methods=['POST'])
def example():
    data = request.get_json()
    if 'name' not in data:
        abort(400, 'Name is required')
    # rest of the code

In the above code, we are checking if the name field is present in the data received. If it’s not present, we are raising a 400 error with a custom message using the abort function.

You can also raise a 400 error without a custom message:

from flask import Flask, abort

app = Flask(__name__)

@app.route('/example', methods=['POST'])
def example():
    data = request.get_json()
    if 'name' not in data:
        abort(400)
    # rest of the code

In this code, we are raising a 400 error without any custom message.

You can also raise a 400 error with a custom response body:

from flask import Flask, abort, jsonify

app = Flask(__name__)

@app.route('/example', methods=['POST'])
def example():
    data = request.get_json()
    if 'name' not in data:
        response = jsonify({'error': 'Name is required'})
        response.status_code = 400
        abort(response)
    # rest of the code

In this code, we are creating a custom response body with an error message and returning it with a 400 error using the abort function.

That’s it! You now know how to return a 400 (Bad Request) response on Flask using «Raising a HTTPException».

Method 2: Return a Custom Response Object

To return a custom response object with a 400 (Bad Request) status code in Flask, you can use the make_response function from the flask module. Here’s an example code snippet:

from flask import Flask, make_response, jsonify

app = Flask(__name__)

@app.route('/example', methods=['POST'])
def example():
    data = request.get_json()
    if 'key' not in data:
        message = {'error': 'Missing key "key" in request body.'}
        response = make_response(jsonify(message), 400)
        return response
    # rest of the code

In the code above, we define a route for a POST request to /example. We then retrieve the request data using request.get_json(). If the data doesn’t contain the key "key", we create a custom response object using make_response and jsonify. We pass the response object a dictionary with an error message and a status code of 400.

Note that we also import the jsonify function from flask. This function serializes data to JSON format and sets the response content type to application/json.

In summary, to return a custom response object with a 400 status code in Flask:

  1. Import the necessary modules (Flask, make_response, and jsonify).
  2. Define a route that handles the request.
  3. Retrieve the request data using request.get_json().
  4. Check if the data contains the required fields.
  5. If the data is invalid, create a custom response object using make_response and jsonify.
  6. Pass the response object a dictionary with an error message and a status code of 400.
  7. Return the response object.

Method 3: Abort with Flask-RESTful Extension

To return a 400 (Bad Request) error with Flask-RESTful Extension, you can use the abort function provided by the extension. Here’s an example code snippet:

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

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

class MyResource(Resource):
    def post(self):
        data = request.get_json()
        if 'name' not in data:
            abort(400, message="Name is required")
        # Your code here

api.add_resource(MyResource, '/my_endpoint')

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

In this example, we define a MyResource class that inherits from Resource. Inside the post method, we get the JSON data from the request and check if it contains a name field. If it doesn’t, we call the abort function with a status code of 400 and a custom error message.

The api.add_resource function is used to map the MyResource class to the /my_endpoint URL.

This is just one example of how to use abort with Flask-RESTful Extension. You can use it in any method of any resource to return an error response with a specific status code and message.

Понравилась статья? Поделить с друзьями:
  • Find эта переменная не определена mathcad ошибка
  • Flatout 2 ошибка cannot open display mode
  • Find linux не выводить ошибки
  • Flask обработка ошибок
  • Final fantasy 7 remake ошибка fatal error