For returning a 400/500 response to clients in a flask webapp, I've seen the following conventions:
Abort
import flask
def index(arg):
return flask.abort("Invalid request", 400)
Tuple
def index(arg):
return ("Invalid request", 400)
Response
import flask
def index(arg):
return flask.Response("Invalid request", 400)
What are the difference and when would one be preferred?
Related question
Coming from Java/Spring, I am used to defining a custom exception with a status code associated with it and then anytime the application throws that exception, a response with that status code is automatically returned to the user (instead of having to explicitly catch it and return a response as shown above). Is this possible in flask? This is my little wrapped attempt
from flask import Response
class FooException(Exception):
""" Binds optional status code and encapsulates returing Response when error is caught """
def __init__(self, *args, **kwargs):
code = kwargs.pop('code', 400)
Exception.__init__(self)
self.code = code
def as_http_error(self):
return Response(str(self), self.code)
Then to use
try:
something()
catch FooException as ex:
return ex.as_http_error()
The best practice is to create your custom exception classes and then registering with Flask app through error handler decorator. You can raise a custom exception from business logic and then allow the Flask Error Handler to handle any of custom defined exceptions. (Similar way it's done in Spring as well.)
You can use the decorator like below and register your custom exception.
#app.errorhandler(FooException)
def handle_foo_exception(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response
You can read more about it here Implementing API Exceptions
Related
I am developing a REST API with python and flask, I leave the project here Github project
I added error handlers to the application but when I run an abort function, it gives me a default message from Flask, not the structure I am defining.
I will leave the path to the handlers and where I run the abort from.
Handlers abort(400)
Flask message
Ok, the solution was told to me that it could be in another question.
What to do is to overwrite the handler function of the Flask Api object.
With that, you can configure the format with which each query will be answered, even the ones that contain an error.
def response_structure(code_status: int, response=None, message=None):
if code_status == 200 or code_status == 201:
status = 'Success'
else:
status = 'Error'
args = dict()
args['status'] = status
if message is not None:
args['message'] = message
if response is not None:
args['response'] = response
return args, code_status
class ExtendAPI(Api):
def handle_error(self, e):
return response_structure(e.code, str(e))
Once the function is overwritten, you must use this new one to create
users_bp = Blueprint('users', __name__)
api = ExtendAPI(users_bp)
With this, we can then use the flask functions to respond with the structure that we define.
if request.args.get('name') is None:
abort(400)
Response JSON
{
"response": "400 Bad Request: The browser (or proxy) sent a request that this server could not understand.",
"status": "Error"
}
I have class which called requests method using getattr like this:
import requests
class CustomRequests(object):
def __init__(self):
pass
def _do_requests(self, method='GET', url='', expected_status=200):
make_request = getattr(requests, method.lower())
url = url if url else 'http://example.com'
try:
response = make_request(method, url=url)
except response.exceptions.RequestException as exception:
raise exception
if response.status_code != expected_status:
raise ValueError
def get(self, *args, **kwargs):
self._do_requests(method='GET', *args, **kwargs)
I am trying to test the api using mock and responses lib like this:
import responses
#responses.activate
def test_get_method(self):
responses.add('GET', url='http://test_this_api.com', status=200)
custom_request = CustomRequest()
response_data = custom_request.get(method='GET')
AssertIsNotNone(response_data)
Is there any better or right way to test this method.
Getting this error:
message = message.format(**values)
KeyError: 'method'
There's no need to use getattr. requests.get, requests.post, etc. are just convenience methods for requests.request, which lets you pass the HTTP method as a parameter:
requests.request('GET', url) # equivalent to requests.get(url)
Also:
Your try/except is pointless, since all you do is re-raise the exception.
It doesn't make sense to raise a ValueError when the response status doesn't match what you expected. ValueError is for "when a built-in operation or function receives an argument that has the right type but an inappropriate value." Create your own exception class, e.g. UnexpectedHTTPStatusError.
Since the whole point of your CustomRequests class seems to be to raise an exception when the status code of the response doesn't match what the user expected, your tests should assert that an exception was actually raised with assertRaises().
I'm writing Falcon middleware for my application. When i get any errors i want to raise error, break process and return my custom response, that looks like:
{
"status": 503,
"message": "No Token found. Token is required."
}
But standard Falcon error implementation does not allow me to set custom fields to my response.
How to solve this problem most properly?
After a lot of time spent, I solved this problem in such interesting way. I put my code in a try/catch block, and when an error is caught I decided not to raise Falcon error, and just tried to write return keyword after setting response status and body, because the method is void, so it does not return anything. Now it looks like:
resp.status = falcon.HTTP_403
resp.body = body
return
I was still looking for an example and here is for anyone who still need it:
from falcon.http_error import HTTPError
class MyHTTPError(HTTPError):
"""Represents a generic HTTP error.
"""
def __init__(self, status, error):
super(MyHTTPError, self).__init__(status)
self.status = status
self.error = error
def to_dict(self, obj_type=dict):
"""Returns a basic dictionary representing the error.
"""
super(MyHTTPError, self).to_dict(obj_type)
obj = self.error
return obj
using:
error = {"error": [{"message": "Auth token required", "code": "INVALID_HEADER"}]}
raise MyHTTPError(falcon.HTTP_400, error)
Create custom exception class explained in falcon docs, search for add_error_handler
class RaiseUnauthorizedException(Exception):
def handle(ex, req, resp, params):
resp.status = falcon.HTTP_401
response = json.loads(json.dumps(ast.literal_eval(str(ex))))
resp.body = json.dumps(response)
Add custom exception class to falcon API object
api = falcon.API()
api.add_error_handler(RaiseUnauthorizedException)
raise falcon.HTTPError(falcon.HTTP_503, 'No Token found. Token is required.')
I am trying to catch the Exception which is raised when the url provided is a messy and wrong url and then return the error response as JSON. This is what i did to implement this logic.
The exception is raised inside the Analysis class when the key_id is not a valid key for S3.
def url_error(status_code, message, reason):
response = jsonify({
'status': status_code,
'message': message,
'reason': reason
})
response.status_code = status_code
return response
class RowColumnCount(Resource):
def get(self, key_id):
try:
rc = Analysis(key_id=key_id)
except S3ResponseError as e:
return url_error(e.status, e.message, e.reason)
json_response = json.loads(rc.count_rows_columns())
return json_response
The above code works fine but its kinda getting repetitive for 50 different Resource classes. Each Resource class should handle this specific error. How to make it a decorator, such that code repetitiveness is reduced.
I am using Flask, Flask-Restful, Python 3.4.3
There are a couple of ways you can achieve what you're trying to do but I think the cleanest way is to extend the Resource class as described in the Flask-Restful docs here and create a new decorator whose job is to catch the S3ResponseError and return the appropriate response. You can then subclass all your resources from your new base Resource class.
Also I would suggest you specify an API level json_output method as described here and here so that way all you have to do is return a dict from any of your resources and they'll be converted to JSON appropriately.
Using a python flask server, I want to be able to throw an http error response with the abort command and use a custom response string and a custom message in the body
#app.errorhandler(400)
def custom400(error):
response = jsonify({'message': error.message})
response.status_code = 404
response.status = 'error.Bad Request'
return response
abort(400,'{"message":"custom error message to appear in body"}')
But the error.message variable comes up as an empty string. I can't seem to find documentation on how to get access to the second variable of the abort function with a custom error handler
If you look at flask/__init__.py you will see that abort is actually imported from werkzeug.exceptions. Looking at the Aborter class, we can see that when called with a numeric code, the particular HTTPException subclass is looked up and called with all of the arguments provided to the Aborter instance. Looking at HTTPException, paying particular attention to lines 85-89 we can see that the second argument passed to HTTPException.__init__ is stored in the description property, as #dirn pointed out.
You can either access the message from the description property:
#app.errorhandler(400)
def custom400(error):
response = jsonify({'message': error.description['message']})
# etc.
abort(400, {'message': 'custom error message to appear in body'})
or just pass the description in by itself:
#app.errorhandler(400)
def custom400(error):
response = jsonify({'message': error.description})
# etc.
abort(400, 'custom error message to appear in body')
People rely on abort() too much. The truth is that there are much better ways to handle errors.
For example, you can write this helper function:
def bad_request(message):
response = jsonify({'message': message})
response.status_code = 400
return response
Then from your view function you can return an error with:
#app.route('/')
def index():
if error_condition:
return bad_request('message that appears in body')
If the error occurs deeper in your call stack in a place where returning a response isn't possible then you can use a custom exception. For example:
class BadRequestError(ValueError):
pass
#app.errorhandler(BadRequestError)
def bad_request_handler(error):
return bad_request(str(error))
Then in the function that needs to issue the error you just raise the exception:
def some_function():
if error_condition:
raise BadRequestError('message that appears in the body')
I hope this helps.
I simply do it like this:
abort(400, description="Required parameter is missing")
flask.abort also accepts flask.Response
abort(make_response(jsonify(message="Error message"), 400))