Custom response during falcon middleware exception - python

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.')

Related

Overriding fastAPI's HTTPException response body

I'm currently writing a few end points for an API in fastAPI.
I'm defining classes that extend fastapi's HTTPException.
The problem is that HTTPException returns a response body with an attribute called detail which will either be a string or a json structure depending on what object you pass to it as seem below.
{
"detail": {
"msg": "error message here"
}
}
{ "detail": "error message here" }
I would like to override this behavior and let it respond with my own structure.
I'm aware that i can install custom exceptions using the exception handler decorator and have that return a JSONResponse object but this is not what i'm looking for.
One option is to set the status code corresponding to your exception, and then return a custom response body.
Here is a simple toy example using this approach to workaround the issue at hand:
from fastapi import FastAPI, Response, status
myapp = FastAPI()
#myapp.get("/")
async def app_func(response: Response, myparam: str):
#end point code here
valid_params = ['a', 'b', 'c']
if myparam not in valid_params:
#Customize logic, status code, and returned dict as needed
response.status_code = status.HTTP_400_BAD_REQUEST
return {'message': 'Oh no! I failed (without detail key)'}
response.status_code = status.HTTP_200_OK
return {'message': 'Yay! I succeeded!'}
A full list of available status codes can be found here.

How to use a decorator in Python to perform authentication?

I am implementing a Flask-RESTFul API where I receive a token from postman and compare it with the token I configured in my code as below:
file: functions.py
def authenticate():
headers = flask.request.headers
bearer = headers.get('Authorization')
token = bearer.split()[1]
try:
if str(hashlib.md5(b'Token_String').hexdigest()) == token:
logger.info('Token authentication successful.')
return True
else:
logger.error('Token autherization failed. Please check the token supplied.')
return False
except Exception as e:
if token is None:
logger.info('Token cannot be null. Supply a token with API call.')
return {'message': 'Token cannot be null. Exception: {error}'.format(error=e)}, 400
else:
logger.info('Token cannot be null. Supply a token with API call.')
return {'message': 'Error reading token. Cannot be Null/Empty. Exception: {error}'.format(error=e)}, 400
This is my API's get method:
class APIClass(Resource):
#classmethod
def get(self):
logger.info('Initiating get()')
if fc.authenticate():
run_some_sql_statements
else:
return {'message': 'Token authentication failed'}, 401
pass
Instead of using an IF-Condition, is there a way I can use the method: authenticate from functions.py file as a decorator on top of my get().
I tried doing this and faced the below error:
from validations import functions as fc
#classmethod
#fc.authenticate
def get(self):
But I see a compilation error: Function 'authenticate' lacks a positional argument
Could anyone let me know what is the mistake I made here and how can I correct it ?
The flask-restful docs contain a section about resource method decorators:
https://flask-restful.readthedocs.io/en/latest/extending.html#resource-method-decorators
class APIClass(Resource):
method_decorators = [authenticate]

How to raise exceptions as json?

At the service level of my application, I am raising an exception and I want it to be printed as JSON to the browser.
I implemented it as stated in the documentation:
raise falcon.HTTPError(
'12345 - My Custom Error',
'some text'
).to_json()
And the output from the console:
TypeError: exceptions must derive from BaseException
Anybody had this issue before and could help me with this one?
You're trying to raise a string. The correct way to do that is with set_error_serializer().
The example from the documentation seems like exactly what you need (plus YAML support).
def my_serializer(req, resp, exception):
representation = None
preferred = req.client_prefers(('application/x-yaml',
'application/json'))
if preferred is not None:
if preferred == 'application/json':
representation = exception.to_json()
else:
representation = yaml.dump(exception.to_dict(),
encoding=None)
resp.body = representation
resp.content_type = preferred
resp.append_header('Vary', 'Accept')
app = falcon.API()
app.set_error_serializer(my_serializer)
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)
import Custom exception class and pass your message
message = {"status": "error", "message" : "Not authorized"}
RaiseUnauthorizedException(message)

Implementing API exception flask-restful

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.

how to get access to error message from abort command when using custom error handler

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))

Categories