Python flask and custom client error messages - python

I'm currently writing a REST API for an app I'm working on. The app is written in python using flask. I have the following:
try:
_profile = profile(
name=request.json['name'],
password=profile.get_salted_password('blablabla'),
email=request.json['email'],
created_by=1,
last_updated_by=1
)
except AssertionError:
abort(400)
session = DatabaseEngine.getSession()
session.add(_profile)
try:
session.commit()
except IntegrityError:
abort(400)
The error handler looks like this:
#app.errorhandler(400)
def not_found(error):
return make_response(standard_response(None, 400, 'Bad request'), 400)
I'm using the error 400 to denote both a problem with a sqlalchemy model validator and a unique constraint when writing to the database and in both cases the following error is sent to the client:
{
"data": null,
"error": {
"msg": "Bad request",
"no": 400
},
"success": false
}
Is there a way to still use abort(400) but also set the error somehow so that the error handler can take care of adding additional information for the error object in the result?
I would like it to be more in line with:
{
"data": null,
"error": {
"msg": "(IntegrityError) duplicate key value violates unique constraint profile_email_key",
"no": 400
},
"success": false
}

you can directly put a custom response in abort() function:
abort(make_response("Integrity Error", 400))
Alternatively, you can put it in the error handler function
#app.errorhandler(400)
def not_found(error):
resp = make_response("Integrity Error", 400)
return resp

errorhandler can take an exception type as well:
#app.errorhandler(AssertionError)
def handle_sqlalchemy_assertion_error(err):
return make_response(standard_response(None, 400, err.message), 400)

i know am late to the game, but for anyone who wants another solution, mine is based on the answer by #codegeek.
i was able to accomplish something similar with the following in my ServerResponse.py module:
def duplicate(message=""):
response = make_response()
response.status_code = 409
response.headers = {
"X-Status-Reason" : message or "Duplicate entry"
}
abort(response)
then i can call
ServerResponse.duplicate('Duplicate submission. An article with a similar title already exists.')
this makes it easy in my AngularJS app to check for a response status and display the X-Status-Reason default or customized message

Related

How can I hide my exception stack trace in python code which is deployed on aws lambda and integrated with API gateway

I use raise Exception (" message ") and it returns this on browser
{"errorMessage": "{"httpStatus": 200, "message": "Exception: {\"httpStatus\": 200, \"message\": \"Exception: [InternalServerError] Project not found\"}"}", "errorType": "Exception", "stackTrace": [the stack trace]}
The stack trace causes security issue
If you are using API gateway in front of lambda. You can set reponse mapping template like below, this will override the response error message and response code as well.
#if($inputRoot.toString().contains('InternalServerError'))
{
"message": "Internal Server Error"
}
#set($context.responseOverride.status = 500)
Alternately you can also catch all the exceptions in the lambda and return whatever you like. However this will not override the status code and you would still get 200 even in case of error.
def handler(event, context):
try:
dosomething(event)
except Exception:
retunrn { "message": "Internal Server error" }
def dosomething(event):
.... You business logic.

Fail AWS lambda in python without exception

I've written a small lambda function and connected it to the API Gateway. I added an error regex to match .*Error.* and return status 400 in the API Gateway. The problem I face is that the regex seems to match only if the lambda failed, as this thread suggests.
My lambda function:
import logging
import boto3
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
if int(event['event_id']) == 1:
return {
'statusCode': 200,
"status": "success"
}
elif int(event['event_id']) == 2:
return {
'statusCode': 400,
"status": "Error"
}
else:
raise Exception("Error")
It looks like case 1 works well, it returns status 200 by default. With event_id=3 it returns status 400 from the API Gateway (with the stack trace in the data, which I would like to avoid), but with event_id=2 it returns status 200 with the Error string in the data.
How can I mark the lambda as failed without throwing an exception?
A failed Lambda means there was some code section in Lambda Function that was failed to be executed. In the current code only the third path has such code section (artificially generated Exception).
It is not possible to execute all your code sections in Lambda and still fail.
If you want to handle API Gateway return status code via program, you'll have to use Lambda Integration. With Lambda integration you may return a response like:
{
"statusCode": "400",
body: json.dumps({ "error": "you messed up!" }),
headers: {
"Content-Type": "application/json",
}
}
and you'll get 400 in API response.

can't seems to read the error message in react js(axios) from flask api (using jsonify)

here is the line of code returning the error message from flask api
return jsonify(message='wrong username or password'),400
reading it from here in react js
axios
.post("http://127.0.0.1:5000/authentication/login", body)
.then((res) => {
console.log(res)
})
.catch((error) => {
console.log(error);
});
and this is what i see in the console
{"message":"Request failed with status code 400","name":"Error","stack":"Error: Request failed with status code 400\n at createError (http://localhost:3000/static/js/1.chunk.js:854:15)\n at settle (http://localhost:3000/static/js/1.chunk.js:1075:12)\n at XMLHttpRequest.handleLoad (http://localhost:3000/static/js/1.chunk.js:329:7)","config":{"url":"http://127.0.0.1:5000/auth/login","method":"post","data":"{\"phone\":\"\",\"password\":\"\"}","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json"},"transformRequest":[null],"transformResponse":[null],"timeout":0,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","maxContentLength":-1}}
The results doesn't not contain the custom message 'wrong username or password'
have actually gotten the solution, something simple
the error data can be accessed from
console.log(error.response.data);
flask by default returns html page instead of json when error is thrown. to make run return normal json as with 200's responses write this in your flask app:
from werkzeug.exceptions import HTTPException
#app.errorhandler(HTTPException)
def handle_exception(e):
"""Return JSON instead of HTML for HTTP errors."""
print(e)
# start with the correct headers and status code from the error
response = e.get_response()
# replace the body with JSON
response.data = json.dumps({
"code": e.code,
"name": e.name,
"description": e.description,
})
response.content_type = "application/json"
return response
then for example for such return in flask
...
return json.dumps(str(e)), 409
and then you can catch in your js:
...
}).then(function(response) {
...
}).catch((error) => {
console.log(error.response.data); // will log your error as string
})

What to do with error "Requested action failed to validate. Please review the Boto exceptions below for more details."?

I'm attempting to create some mock tests for some in-house AWS scripts. Upon creating the boto3 resource and attempting to validate a non-existent stack, I get the following error when I run my python script:
"The requested action failed to validate. Please review the Boto exceptions below for more details."
This would be great except....there is no "Boto exceptions below" like the error mentions, just the command prompt. What does this mean?
Here is some example code:
def function():
resource = boto3.resource('cloudformation')
verify_stack(resource, 'MockStack')
def verify_stack(session, stackName):
try:
stack = session.meta.client.describe_stacks(StackName=stack_name)
except:
return {}
else:
return stack
Just for reference, the stack "MockStack" doesn't exist, and I'm testing to make sure of this. A part of me thinks the error I'm getting is telling me that the stack doesn't exist, which would be correct. Could this be the issue?
Found the solution to my issue. It was a ClientError that I caught by wrapping the verify_stack function in a try/except statement. Here is the correct code:
try:
session = boto3.Session()
resource = session.resource('cloudformation')
sqs_template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"QueueGroup": {
"Type": "AWS::SQS::Queue",
"Properties": {
"QueueName": "my-queue",
"VisibilityTimeout": 60,
}
},
},
}
sqs_template_json = json.dumps(sqs_template)
resource.create_stack(
StackName="mock_stack",
TemplateBody=sqs_template_json,
)
response = resource.verify_stack(resource, 'mock_stack')
print response
except ClientError as error:
print error.message

Custom exception message from google endpoints exception

Currently, I've a Python endpoints service to change the name of a user. If there is no problem, I return a MessageField with a lot of informations.
But sometimes, the request is correct and I want to say to client that there is an error that he can handle : "Hey, sorry but there is already a user with this name", or also, "Hey, sorry but you have already change your name today !".
The problem is, when I raise an endpoint exception like a UnauthorizedException or anything else, I can just put a custom message :
raise endpoints.UnauthorizedException('Invalid user_id or auth_token !')
result in :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "required",
"message": "Invalid user_id or auth_token !",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid user_id or auth_token !"
}
}
Is there a way to really customize this message ? Or to return a completely different MessageField in this case ?
For example, I would like to return a JSON like this (maybe with HTTP Code 400) :
{
"error": {
"username_already_exist": 1
}
}
Thanks !
If you don't want to return the JSON that is build with an exception, you'll have to do a straight up return in your code instead of raising an exception. You can build the JSON you mention and then return that whenever you hit the "exception point".
Or what you can do is use the message to send the error to the client, then have the client catch that exception, parse appropriately, and display whatever you want.

Categories