# Exception Filters
Exception filters are a powerful mechanism in Serinus that allow you to handle exceptions thrown during the request-response cycle. They provide a way to catch and process exceptions, enabling you to return custom error responses or perform specific actions when an error occurs.
```dart
import 'package:serinus/serinus.dart';
class NotFoundExceptionFilter extends ExceptionFilter {
NotFoundExceptionFilter() : super(catchTargets: [NotFoundException]);
@override
Future onException(ExecutionContext context, Exception exception) async {
if (exception is NotFoundException) {
context.response.statusCode = 404;
context.response.body = {'message': 'Resource not found'};
}
}
}
```
In the example above, we create a custom exception filter that catches `NotFoundException` and returns a 404 status code with a custom message.
## Throwing standard exceptions
Serinus provides a built-in `SerinusException` class that you can use to handle errors in your application. This class is the base class for all exceptions in Serinus and provides a way to define custom exceptions with specific status codes and messages.
For example, in the `UserController` class, you can throw a `SerinusException` when the user is not found:
```dart
import 'package:serinus/serinus.dart';
class UserController extends Controller {
UserController() : super('/users') {
on(Route.get('/'), getUser);
}
Future getUser(RequestContext context) async {
final userId = context.params['id'];
final user = await context.use().getUserById(userId);
if (user == null) {
throw SerinusException('User not found', statusCode: 404);
}
return user;
}
}
```
## Built-in Exceptions
Serinus, by default, provides a set of built-in exceptions that you can use in your application. These exceptions are subclasses of `SerinusException` and are designed to handle common HTTP error scenarios. You can use these exceptions to simplify error handling in your application.
These exceptions are automatically mapped to HTTP status codes, so you don't have to worry about manually setting the status code for each exception.
| Exception | Description | Status Code |
| --- | --- | --- |
| `BadRequestException` | Thrown when the request is invalid. | 400 |
| `UnauthorizedException` | Thrown when the user is not authorized to access the resource. | 401 |
| `ForbiddenException` | Thrown when the user is not allowed to access the resource. | 403 |
| `NotFoundException` | Thrown when the requested resource is not found. | 404 |
| `MethodNotAllowedException` | Thrown when the method is not allowed on the resource. | 405 |
| `ConflictException` | Thrown when there is a conflict with the current state of the resource. | 409 |
| `GoneException` | Thrown when the requested resource is no longer available. | 410 |
| `PreconditionFailedException` | Thrown when the requested resource is no longer available. | 412 |
| `PayloadTooLargeException` | Thrown when the request payload is too large. | 413 |
| `UnsupportedMediaTypeException` | Thrown when the media type is not supported. | 415 |
| `UnprocessableEntityException` | Thrown when the request is valid, but the server cannot process it. | 422 |
| `TooManyRequestsException` | Thrown when the client has sent too many requests in a given amount of time. | 429 |
| `InternalServerErrorException` | Thrown when an internal server error occurs. | 500 |
| `NotImplementedException` | Thrown when the requested feature is not implemented. | 501 |
| `BadGatewayException` | Thrown when the gateway is bad. | 502 |
| `ServiceUnavailableException` | Thrown when the service is unavailable. | 503 |
| `GatewayTimeoutException` | Thrown when the gateway times out. | 504 |
All these exceptions have a `message` field that you can use to provide a custom error message.
```dart
throw BadRequestException('Invalid request format');
```
## Creating a Custom Exception
Let's say you need to add another field to the exception, such as a `errors` field. You can create a custom exception class that extends `SerinusException` and adds the new field:
```dart
import 'package:serinus/serinus.dart';
class CustomException extends SerinusException {
final List errors;
CustomException(String message, {required this.errors, int statusCode = 400})
: super(message, statusCode: statusCode);
}
```
And as before, you can throw this exception in your controller:
```dart
import 'package:serinus/serinus.dart';
class UserController extends Controller {
UserController() : super('/users') {
on(Route.get('/'), getUser);
}
Future getUser(Request request) async {
final userId = request.params['id'];
final user = await context.use().getUserById(userId);
if (user == null) {
throw CustomException('User not found', errors: ['User with id $userId not found'], statusCode: 404);
}
return user;
}
}
```
## Binding Exception Filters
You can bind exception filters at different levels in your application: globally, at the controller level, or at the route level.
### Global Exception Filters
You can bind an exception filter globally to your application using the `use` method on the `SerinusApplication` object.
```dart
import 'package:serinus/serinus.dart';
Future main() async {
final app = await serinus.createApplication(
entrypoint: AppModule()
);
// Add the exception filter to the application
app.use(NotFoundExceptionFilter());
// Start the application
await app.serve();
}
```
### Controller Exception Filters
You can also bind an exception filter to a specific controller using the `exceptionFilters` property of the `Controller` class.
```dart
import 'package:serinus/serinus.dart';
class UserController extends Controller {
List get exceptionFilters => [NotFoundExceptionFilter()];
UserController() : super('/users') {
on(Route.get('/'), getUser);
}
Future getUser(RequestContext context) async {
final userId = context.params['id'];
final user = await context.use().getUserById(userId);
if (user == null) {
throw SerinusException('User not found', statusCode: 404);
}
return user;
}
}
```
### Route Exception Filters
You can also bind an exception filter to a specific route using the `exceptionFilters` property of the `Route` class.
```dart
import 'package:serinus/serinus.dart';
class UserController extends Controller {
UserController() : super('/users') {
on(
Route.get(
'/',
exceptionFilters: {NotFoundExceptionFilter()}
),
getUser
);
}
Future getUser(RequestContext context) async {
final userId = context.params['id'];
final user = await context.use().getUserById(userId);
if (user == null) {
throw SerinusException('User not found', statusCode: 404);
}
return user;
}
}
```