Error Handling¶
Note
Take a look at Sanic’s Exceptions documentation to better understand how Insanic’s error handling works.
Insanic’s error handling is done with Sanic’s error handling functionality, but with Insanic’s own exception and error definitions. Before we move onto the components that comprise of an Insanic exception, let’s take a look at a quick example.
# in example/app.py
from insanic import Insanic
from insanic.conf import settings
from insanic.errors import GlobalErrorCodes
from insanic.exceptions import APIException
__version__ = '0.1.0'
settings.configure()
app = Insanic('example', version=__version__)
@app.route('/help')
def help_view(request, *args, **kwargs):
raise APIException("Help me! Something blew up!",
error_code=GlobalErrorCodes.error_unspecified,
status_code=400)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
With this piece of code, let’s try running it…
$ python app.py
Now by sending a request to the server…
curl -i http://0.0.0.0:8000/help
HTTP/1.1 400 Bad Request
Content-Length: 139
Content-Type: application/json
Connection: keep-alive
Keep-Alive: 60
{
"message":"An unknown error occurred",
"description":"Help me! Something blew up!",
"error_code":{
"name":"insanic_error_unspecified",
"value":999998
}
} # response was formatted for readability
From the response there are a couple components we need to cover to understand how Insanic’s error handler works.
The
GlobalErrorCodes
The
APIException
The response.
1. Error Codes¶
In a distributed system, errors can happen anywhere. It can happen within the service you have created, it could happen down the road where you made a request to another service for some additional information, or even worse, the other service could get a different error message from a request that it had to make to aggregate the response.
As a result, the only way to keep track and possibly debug the situation, specific pin point traceability was very important. Of course, just returning a 400 Bad Request error response might suffice, but in some instances, an application may have to react in a certain manner if it receives a particular 400 Bad Request error. For example, rolling back a database commit only for a specific error.
Insanic provides common error codes, accessible in insanic.errors.GlobalErrorCodes
but each service may provide their own specific error codes with one restriction.
The Error Code must be an Enum
type.
To create your own:
# example/errors.py
from enum import Enum
class MyErrorCodes(Enum):
not_going_fast_enough = 10001
too_slow = 10002
help_me = 10003
When set to the error_code
attribute in the Insanic’s
exception (we will get to this a bit later), the enum will be unpacked
by Insanic’s Error Handler to a JSON object. So in our example,
MyErrorCodes.not_going_fast_enough
will be unpacked like so:
{
"name":"not_going_fast_enough",
"value":10001
}
2. Insanic APIException¶
To actually create the error, Insanic provides its own APIException
base class for its own
error handling. This exception will create the response as shown in the first example.
There are 4 attributes to the exception.
status_code
: an integer representing the status code of the response.description
: a string with human readable description of the error.error_code
: an Enum as explained in the ErrorCode section above.message
: a string with a general message.
There are several exceptions provided as base templates, but it is up to the developer to define how detailed the exceptions will be.
Let’s create some example execeptions:
# example/exceptions.py
from insanic import status
from insanic.exceptions import APIException, BadRequest
from .errors import MyErrorCodes
class TooSlowException(APIException):
status_code = status.HTTP_408_REQUEST_TIMEOUT
description = "Too slow!"
error_code = MyErrorCodes.too_slow
class MyBadRequest(BadRequest):
error_code = MyErrorCodes.not_going_fast_enough
And now to use these exceptions…
# example/views.py
from insanic import status
from insanic.exceptions import APIException
from .app import app # your insanic application
from .errors import MyErrorCodes
from .exceptions import TooSlowException
@app.route('/too_slow`)
def too_slow_view(request, *args, **kwargs):
raise TooSlowException()
@app.route('/very_slow')
def very_slow_view(request, *args, **kwargs):
raise TooSlowException("This is very slow!")
@app.route('/help_me_too_slow')
def help_me_too_slow(request, *args, **kwargs):
raise APIException(
"HELP ME!",
error_code=MyErrorCodes.help_me,
status_code=status.HTTP_504_GATEWAY_TIMEOUT
)
3. Putting ErrorCodes and Exceptions together¶
With exceptions and error codes defined, Insanic’s error handler will serialize the exception to the error response structure as shown in the example.
class TooSlowException(APIException):
status_code = status.HTTP_408_REQUEST_TIMEOUT
description = "Too slow!"
error_code = MyErrorCodes.too_slow
With this exception we created above, Insanic’ Error Handler will create this response.
{
"message":"An unknown error occurred",
"description":"Too slow!",
"error_code":{
"name":"too_slow",
"value":10002
}
}
The
status_code
is the status code of the response.The
description
is the description.The
message
is the message attribute in APIException.The
error_code
is the unpacked enum.
What about NON-Insanic Exceptions?¶
Any Sanic Exceptions will automatically be converted to an Insanic Exception and will try and serialize the message into Insanic’s error message format.
from sanic.exceptions import ServiceUnavailable
@app.route('/sanic')
def raise_sanic(request, *args, **kwargs):
raise ServiceUnavailable('sanic error')
Will result in…
$ curl -i http://0.0.0.0:8000/sanic
HTTP/1.1 503 Service Unavailable
Content-Length: 126
Content-Type: application/json
Connection: keep-alive
Keep-Alive: 60
{
"message":"Service Unavailable",
"description":"sanic error",
"error_code":{
"name":"insanic_error_unspecified",
"value":999998
}
}
Any NON-Insanic and NON-Sanic exceptions raised during the process of a request
will default to a 500 Internal Server Error
.
@app.route('/builtin')
def raise_sanic(request, *args, **kwargs):
raise SystemError('sanic error')
$ curl -i http://localhost:8000/builtin
HTTP/1.1 500 Internal Server Error
Content-Length: 167
Content-Type: application/json
Connection: keep-alive
Keep-Alive: 60
{
"message":"Server Error",
"description":"Something has blown up really bad. Somebody should be notified?",
"error_code":{
"name":"insanic_unknown_error",
"value":999999
}
}
See Also…¶
Refer to the insanic.errors module for insanic’s ErrorCodes.
Refer to the insanic.exceptions module for Insanic’s Exceptions.
Refer to the insanic.status module for easy status codes.