简体   繁体   中英

Flask API requests returning old Errors

I'm developing a Flask API that will read data from a cloud database.

I've noticed some strange behaviour in my error handling.

When I make a request that returns an error, and after that make a correct request, the error json will be returned once again in the correct request. Only in the body though, the API status code is correct (and the flask application doesn't throw an error at all, in fact it doesn't even seem to call the error handling module).

If I change the error from for example an invalid queryparam request to a 404 request, the error will change in the json, and the latest error that was returned will persist through all subsequent requests until I rerun the application.

I have an error handling module that I've registered in my app:

from server.errorhandling.errors import InvalidQueryParameterError
from flask import Blueprint, jsonify, request
from server.common.jsonresponse import set_error_jsonresponse
import traceback

handle_errors = Blueprint('errorhandler', __name__)

@handle_errors.app_errorhandler(InvalidQueryParameterError)
def handle_notfound_error(error):
    traceback.print_exc()
    message = [str(x) for x in error.args]
    status_code = 400
    response = set_error_jsonresponse(message,error.__class__.__name__)
    return jsonify(response), status_code

@handle_errors.app_errorhandler(404)
def handle_notfound_error(error):
    traceback.print_exc()
    message = [str(x) for x in error.args]
    status_code = 404
    response = set_error_jsonresponse(message,error.__class__.__name__)
    return jsonify(response), status_code

Registering in my app:

self.register_blueprint(errorhandler.handle_errors)

And I have a module for setting the json response, that is used both by the "normal" successful response code, and the errorhandling:

from flask import current_app,request,Blueprint

# - Static template for response json -
response_body = {
                        "domain": {
                            "datatype": {
                                "data": {
                                }
                            }
                        }
                    }

def set_jsonresponse(query_jobs):
    for job in query_jobs:
        response_tuple = queryjob_to_response_tuple(job)
        response_data = response_tuple
        response_obj = DataResponse(response_data)
        response_body['domain']['datatype']['data'][get_data_entity_kvm(response_obj)[0]] = get_data_entity_kvm(response_obj)[1]
        success_response_body = response_body
    return success_response_body

def set_error_jsonresponse(message="An unexpected error has occurred.",errortype="InternalError"):
    error_body = {
                    "error":{
                        "message":message,
                        "type":errortype
                        }
                 }
    response_body['domain']['datatype'].update(error_body)
    return response_body

Everything else seems to be working as expected. When I debug the code, the problem appears in the row where I set the response_body. But I don't think how I set the response in the code is the problem itself, I suspect the problem has more to do with some background-storing of response data that I haven't understood yet.

Though the issue is similar, I have tried the answer here: API serving up old responses in flask And it does not work. I've tried both sending the no-cache header values and hardcoding them in my application.

What am I not getting here?

You're defining response_body as a global variable.

# - Static template for response json -
response_body = {
                        "domain": {
                            "datatype": {
                                "data": {
                                }
                            }
                        }
                    }

In the set_error_jsonresponse call, you update this global variable, then return it:

response_body['domain']['datatype'].update(error_body)

Any data stored within this structure is still there on subsequent requests, because a global variable is within the worker's memory.

I'm not sure what get_data_entity_kvm is, but it looks like in the set_jsonresponse function you edit the global again, without removing the error_body which was added previously.

    response_body['domain']['datatype']['data'][get_data_entity_kvm(response_obj)[0]] = get_data_entity_kvm(response_obj)[1]

Hence seeing the error message from the last request, even on subsequent successful requests. The status codes are correct because they are defined in the handler functions. When you restart the app the response_body is set back to the original.


The quick fix for this is not to define response_body as a global. So you could have a function like:

def get_response_body():
    return {
        "domain": {
            "datatype": {
                "data": {
                }
            }
        }
    }

Then in each function where it's required, something like:

def set_error_jsonresponse(message="An unexpected error has occurred.",errortype="InternalError"):
    response_body = get_response_body()
    # do something with that...

I also suggest investigating the Flask deployment docs, and learn about WSGI servers. In a production setup, a WSGI server like gunicorn may have sevaral syncronous workers. In that configuration the behaviour observed from this app would be even stranger: The same as what you're seeing now, but non-consistent, as subsequent requests may be handled by any of the multiple workers, each with their own copy of the response_body global. This is because globals are not threadsafe .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM