Skip to content

Commit cd560b3

Browse files
author
Ezeudoh Tochukwu
committed
more on exception doc update
1 parent b729766 commit cd560b3

File tree

1 file changed

+86
-93
lines changed

1 file changed

+86
-93
lines changed

docs/overview/exception_handling.md

Lines changed: 86 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
# **Exceptions & Exception Handling**
2-
Ellar comes with a built-in exceptions middleware, `ExceptionMiddleware`, which is responsible for processing all exceptions across
3-
an application. When an exception is not handled by your application code, it is caught by this middleware, which
4-
then automatically sends an appropriate user-friendly response .
2+
Ellar includes a built-in exceptions middleware, known as `ExceptionMiddleware`, responsible for processing all exceptions
3+
that occur within an application. When an exception goes unhandled by your application code,
4+
it is intercepted by this middleware, which then automatically sends an appropriate, user-friendly response.
55

66
```json
77
{
88
"status_code": 403,
99
"detail": "Forbidden"
1010
}
1111
```
12-
And based on application config `DEBUG` value, the exception is raised during is `config.DEBUG`
13-
is True but when `config.DEBUG` a 500 error is returned as shown below:
12+
Depending on the application's configuration and the value of `DEBUG`, the exception handling behavior differs.
13+
When `config.DEBUG` is True, the exception that is raised is shown to the client for easy error debugging.
14+
However, when `config.DEBUG` is False, a 500 error is returned to the client, as illustrated below:
15+
1416
```json
1517
{
1618
"statusCode": 500,
1719
"message": "Internal server error"
1820
}
1921
```
2022

21-
Types of exceptions types managed by default:
23+
Ellar manages various types of exceptions by default:
2224

2325
- **`HTTPException`**: Provided by `Starlette` to handle HTTP errors.eg. `HTTPException(status_code, detail=None, headers=None)`
2426
- **`WebSocketException`**: Provided by `Starlette` to manage websocket errors. eg `WebSocketException(code=1008, reason=None)`
2527
- **`RequestValidationException`**: Provided by `Pydantic` for validation of request data
26-
- **`APIException`**: It is a type of exception for REST API based applications. It gives more concept to error and provides a simple interface for creating other custom exception needs in your application without having to create an extra exception handler.
28+
- **`APIException`**: It is a type of exception designed for REST API-based applications. It offers a more conceptual approach to handling errors and provides a simple interface for creating other custom exceptions in your application without requiring an additional exception handler.
2729

2830
For example,
2931

@@ -38,8 +40,8 @@ Types of exceptions types managed by default:
3840
```
3941

4042
### **Built-in APIExceptions**
41-
Ellar provides a set of standard exceptions that inherit from the base `APIException`.
42-
These are exposed from the `ellar.common` package, and represent many of the most common HTTP exceptions:
43+
Ellar offers a set of standard exceptions that inherit from the base `APIException`.
44+
These exceptions are available within the `ellar.common` package and represent many of the most common HTTP exceptions:
4345

4446
- `AuthenticationFailed`
4547
- `ImproperConfiguration`
@@ -51,10 +53,11 @@ These are exposed from the `ellar.common` package, and represent many of the mos
5153
- `UnsupportedMediaType`
5254

5355
## **Throwing standard exceptions**
54-
Let's use this `ServiceUnavailableException` in our previous project.
56+
Let's use the `ServiceUnavailableException` in our previous project.
5557

5658
For example, in the `CarController`, we have a `get_all()` method (a `GET` route handler).
57-
Let's assume that this route handler throws an exception for some reason. To demonstrate this, we'll hard-code it as follows:
59+
Let's assume that this route handler throws an exception for some reason.
60+
To demonstrate this, we'll hard-code it as follows:
5861

5962
```python
6063
# project_name/apps/car/controllers.py
@@ -70,13 +73,14 @@ Now, when you visit [http://127.0.0.1/car/](http://127.0.0.1/car/){target="_blan
7073
"detail": "Service Unavailable"
7174
}
7275
```
76+
When we raise the `ServiceUnavailableException`, it produces a `JSON` response because `Ellar` has implemented
77+
an exception handler for any `APIException` exception type. We will see how to change the default exception handler later.
78+
79+
Another error presentation is available on the APIException instance:
7380

74-
When we raise the `ServiceUnavailableException` exception type, it produces a `JSON` response because `Ellar` has implemented an exception handler for any `APIException` exception type.
75-
We will see how to change the default exception handler later.
81+
- `.detail`: returns the textual description of the error.
82+
- `get_full_details()`: returns both the textual description and other information about the error.
7683

77-
There is another error presentation available on the `APIException` instance:
78-
- `.detail`: returns textual description of the error.
79-
- `get_full_details()`: returns both textual description and other information about the error.
8084

8185
```shell
8286
>>> print(exc.detail)
@@ -85,16 +89,12 @@ Service Unavailable
8589
{'detail':'Service Unavailable','code':'service_unavailable', 'description': 'The server cannot process the request due to a high load'}
8690
```
8791

88-
!!!hint
89-
You should only raise `HTTPException` and `APIException` during route function handling. Since exception manager is a
90-
middleware and `HTTPException` raised before the `ExceptionMiddleware` will not be managed. Its advice exceptions happening
91-
inside middleware classes should return appropriate responses directly.
92-
9392

9493
## **Exception Handlers**
95-
Exception Handlers are classes or functions that handles specific exception type response generation.
94+
Exception Handlers are classes or functions that handles what response that is returned to the client for specific exception types.
95+
96+
Here is an example of an ExceptionHandler that handles `HTTPException` in the application:
9697

97-
Below is an example of ExceptionHandler that handles `HTTPException` in the application:
9898
```python
9999
import typing as t
100100

@@ -126,15 +126,17 @@ class HTTPExceptionHandler(IExceptionHandler):
126126
data, status_code=exc.status_code, headers=exc.headers
127127
)
128128
```
129-
In the example above, `HTTPExceptionHandler.catch` method will be called when `ExeceptionMiddleware` detect exception of type `HTTPException`.
130-
And its return response to the client.
129+
In the example above, `HTTPExceptionHandler` will be registered in a key-value data structure of exception handlers.
130+
Where `exception_type_or_code` is the key and the `HTTPExceptionHandler` class is the value.
131131

132+
During exception handling, `HTTPExceptionHandler.catch` method will be called when `ExceptionMiddleware` detect an exception of type `HTTPException`.
133+
And then, a JSON response is created and returned to the client.
132134

133-
## **Creating Custom Exception Handler**
134135

135-
To create an exception handler for your custom exception, you have to make a class that follows the `IExceptionHandler` contract.
136+
## **Creating Custom Exception Handler**
137+
To create an exception handler for your custom exception, you need to create a class that follows the `IExceptionHandler` contract.
136138

137-
At the root project folder, create a file `custom_exceptions.py`,
139+
At the root project folder, create a file named `custom_exceptions.py`:
138140

139141
```python
140142
# project_name/custom_exceptions.py
@@ -162,11 +164,12 @@ class MyCustomExceptionHandler(IExceptionHandler):
162164

163165
**IExceptionHandler** Overview:
164166

165-
- `exception_type_or_code`: defines the `exception class` OR `status code` to target when resolving exception handlers.
166-
- `catch()`: defines the handling code and response to be returned to the client.
167+
- `exception_type_or_code`: defines the `exception class` OR `status code` as a key to identify an exception handler.
168+
- `catch()`: define the exception handling code and response a `Response` object to be returned to the client.
167169

168170
### **Creating Exception Handler for status code**
169-
Let's create a handler for `MethodNotAllowedException` which, according to the HTTP code is `405`.
171+
Let's create a handler for the `MethodNotAllowedException`, which corresponds to the HTTP status code `405`.
172+
170173
```python
171174
# project_name/apps/custom_exceptions.py
172175
import typing as t
@@ -199,60 +202,61 @@ class ExceptionHandlerAction405(IExceptionHandler):
199202
context_kwargs = {}
200203
return render_template('405.html', request=ctx.switch_to_http_connection().get_request(), **context_kwargs)
201204
```
202-
We have registered a handler for any `HTTP` exception with a `405` status code which we are returning a template `405.html` as a response.
205+
In this code snippet, we've registered a handler for any `HTTP` exception with a `405` status code, and we return a template, `405.html`, as a response.
206+
Similarly, you can create an exception handler for the `500` status code that returns an HTML template.
203207

204208
!!!info
205-
Ellar will look for `405.html` in all registered modules. So `car` folder, create a `templates` folder and add `405.html`.
206-
207-
The same way can create Handler for `500` error code.
209+
Ellar will search for `405.html` in all registered modules. So, within the `car` folder, create a templates folder and add `405.html`.
208210

209211

210212
## **Registering Exception Handlers**
211213
We have successfully created two exception handlers `ExceptionHandlerAction405` and `MyCustomExceptionHandler` but they are not yet visible to the application.
212214

213-
- `config.py`: The config file holds manage application settings. The `EXCEPTION_HANDLERS` variable defines all custom exception handlers registered to `ExceptionMiddlewareService` when bootstrapping the application.
214-
215-
```python
216-
# project_name/config.py
217-
import typing as t
218-
from ellar.core import ConfigDefaultTypesMixin
219-
from ellar.common import IExceptionHandler
220-
from .apps.custom_exceptions import MyCustomExceptionHandler, ExceptionHandlerAction405
215+
=== "Using config.py"
216+
In the `config.py` file, which holds application settings, you can define custom exception handlers to be registered with the `ExceptionMiddlewareService` during the application's bootstrapping process.
217+
218+
```python
219+
# project_name/config.py
220+
import typing as t
221+
from ellar.core import ConfigDefaultTypesMixin
222+
from ellar.common import IExceptionHandler
223+
from .apps.custom_exceptions import MyCustomExceptionHandler, ExceptionHandlerAction405
224+
225+
class BaseConfig(ConfigDefaultTypesMixin):
226+
EXCEPTION_HANDLERS: t.List[IExceptionHandler] = [
227+
MyCustomExceptionHandler(),
228+
ExceptionHandlerAction405()
229+
]
230+
```
231+
=== "Using Application Instance"
232+
Alternatively, you can add exception handlers directly through the app instance in your application:
221233

222-
class BaseConfig(ConfigDefaultTypesMixin):
223-
EXCEPTION_HANDLERS: t.List[IExceptionHandler] = [
224-
MyCustomExceptionHandler(),
234+
```python
235+
# project_name/server.py
236+
237+
import os
238+
239+
from ellar.common.constants import ELLAR_CONFIG_MODULE
240+
from ellar.core.factory import AppFactory
241+
from .root_module import ApplicationModule
242+
from .apps.custom_exceptions import MyCustomExceptionHandler, ExceptionHandlerAction405
243+
244+
application = AppFactory.create_from_app_module(
245+
ApplicationModule,
246+
config_module=os.environ.get(
247+
ELLAR_CONFIG_MODULE, "project_name.config:DevelopmentConfig"
248+
),
249+
)
250+
251+
application.add_exception_handler(
252+
MyCustomExceptionHandler(),
225253
ExceptionHandlerAction405()
226-
]
227-
```
228-
- `application instance`: You can also add exception through `app` instance.
229-
230-
```python
231-
# project_name/server.py
232-
233-
import os
234-
235-
from ellar.common.constants import ELLAR_CONFIG_MODULE
236-
from ellar.core.factory import AppFactory
237-
from .root_module import ApplicationModule
238-
from .apps.custom_exceptions import MyCustomExceptionHandler, ExceptionHandlerAction405
239-
240-
application = AppFactory.create_from_app_module(
241-
ApplicationModule,
242-
config_module=os.environ.get(
243-
ELLAR_CONFIG_MODULE, "project_name.config:DevelopmentConfig"
244-
),
245-
)
246-
247-
application.add_exception_handler(
248-
MyCustomExceptionHandler(),
249-
ExceptionHandlerAction405()
250-
)
251-
```
254+
)
255+
```
252256

253257
## **Override Default Exception Handler**
254-
We have seen how to create an exception handler for status codes and specific exception types.
255-
To override any exception handler, it follows the same pattern and then defines the target to exception type
258+
We have seen how to create an exception handler for status codes or for a specific exception type.
259+
The same applies to when we want to override an existing exception handler in Ellar project.
256260

257261
For example:
258262

@@ -274,13 +278,12 @@ class OverrideAPIExceptionHandler(IExceptionHandler):
274278
{'message': exc.detail}, status_code=exc.status_code,
275279
)
276280
```
277-
278-
Once we register the `OverrideAPIExceptionHandler` exception handler, it will become the default handler for the `APIException` exception type.
281+
Once you register the `OverrideAPIExceptionHandler` exception handler, it will become the default `handler` for the `APIException` exception type.
279282

280283
## **Declaring Exception Handler as a function**
281-
In the previous section, we have seen how to create a custom ExceptionHandler from `IExceptionHandler`. In this section we will do the same using a plane function.
284+
In the previous section, we saw how to create a custom ExceptionHandler from `IExceptionHandler`. In this section, we'll achieve the same result using a plain function.
282285

283-
For example, lets say we have a function `exception_handler_fun` as shown below
286+
For example, let's say we have a function `exception_handler_fun` as shown below:
284287

285288
```python
286289
from starlette.responses import PlainTextResponse
@@ -290,8 +293,7 @@ from ellar.common import IExecutionContext
290293
def exception_handler_fun(ctx: IExecutionContext, exc: Exception):
291294
return PlainTextResponse('Bad Request', status_code=400)
292295
```
293-
294-
To get the `exception_handler_fun` to work as an ExceptionHandler, you will need `CallableExceptionHandler` from `ellar.common.exceptions` package.
296+
To make the `exception_handler_fun` work as an ExceptionHandler, you will need to use `CallableExceptionHandler` from the `ellar.common.exceptions` package:
295297

296298
```python
297299
from starlette.responses import PlainTextResponse
@@ -307,20 +309,11 @@ exception_400_handler = CallableExceptionHandler(
307309
exc_class_or_status_code=400, callable_exception_handler=exception_handler_fun
308310
)
309311
```
310-
In the above example, you have created `exception_400_handler` Exception Handler to handler http exceptions with status code 400.
311-
And then it can be registed as an exception handler as we did in previous section
312+
In the example above, you have created an `exception_400_handler` Exception Handler to handle HTTP exceptions with a status code of 400.
313+
You can then register it as an exception handler in your configuration, as we did in the previous section:
312314

313-
```python
314-
from .custom_exception_handlers import exception_400_handler
315-
316-
317-
class BaseConfig(ConfigDefaultTypesMixin):
318-
EXCEPTION_HANDLERS: List[IExceptionHandler] = [
319-
exception_400_handler
320-
]
321-
```
315+
Additionally, `exception_handler_fun` can be configured to handle a custom exception type, as shown below:
322316

323-
Also, `exception_handler_fun` can be made to handle an custom exception type as shown below.
324317
```python
325318
from starlette.responses import PlainTextResponse
326319
from ellar.common import IExecutionContext
@@ -339,4 +332,4 @@ exception_custom_handler = CallableExceptionHandler(
339332
exc_class_or_status_code=CustomException, callable_exception_handler=exception_handler_fun
340333
)
341334
```
342-
In the above example, `exception_custom_handler`
335+
In this example, `exception_custom_handler` is configured to handle a custom exception type, CustomException.

0 commit comments

Comments
 (0)