A guard decides whether a request may reach the endpoint. Use one for authentication, role checks, and permissions. Guards run after all
middleware, so they can read anything middleware stored in
Data. The example below uses the
LoadUser middleware from Writing a LifecycleComponent.
Example#
import 'package:revali_router/revali_router.dart';
class RequireRole implements LifecycleComponent {
const RequireRole(this.role);
final String role;
GuardResult check(Data data) {
final user = data.get<User>();
if (user == null) {
return const GuardResult.block(statusCode: 401, body: 'Sign in first');
}
if (user.role != role) {
return GuardResult.block(body: 'Requires the $role role');
}
return const GuardResult.pass();
}
}
import 'package:revali_router/revali_router.dart';
@LifecycleComponents([LoadUser]) // middleware that stores the User in Data
@RequireRole('admin')
@Controller('admin')
class AdminController {
const AdminController();
@Get('stats')
String stats() => 'ok';
}
| Caller | Response |
|---|---|
| No user loaded | 401 Sign in first |
A user without the admin role | 403 Requires the admin role |
| An admin | 200 {"data":"ok"} |
In debug mode, the blocked responses also have a __DEBUG__ block appended.
Results#
| Result | Effect |
|---|---|
GuardResult.pass() |
Continue to the next guard, then to the interceptors and the endpoint. |
GuardResult.block({statusCode, headers, body}) |
End the request. The status defaults to 403. |
The method can be async and return Future<GuardResult>. block
takes the same arguments as the other error results. See Error Responses.
You can also throw an exception from a guard and map it to a response with an exception catcher. That keeps the error format in one place when several guards fail the same way.
Classic Style#
As an alternative, implement Guard and its protect method:
import 'package:revali_router/revali_router.dart';
class AdminGuard implements Guard {
const AdminGuard();
@override
Future<GuardResult> protect(Context context) async {
final user = context.data.get<User>();
return user?.role == 'admin'
? const GuardResult.pass()
: const GuardResult.block();
}
}
Apply it with @AdminGuard(), or by type with @Guards([AdminGuard]).