Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions example/app_exception_handler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'package:vania/src/exception/database_exception.dart';
import 'package:vania/src/exception/exception_handler.dart';
import 'package:vania/src/exception/not_found_exception.dart';
import 'package:vania/src/exception/query_exception.dart';
import 'package:vania/src/exception/validation_exception.dart';
import 'package:vania/src/http/request/request.dart';
import 'package:vania/src/http/response/response.dart';
import 'package:vania/src/service/service_provider.dart';
import 'package:vania/application.dart';

class DatabaseExceptionHandler extends ExceptionHandler<DatabaseException> {
@override
Response handle(DatabaseException exception, Request? request) {
return Response.json({
'success': false,
'message': 'Database error occurred',
'error': exception.message,
}, 500);
}
}

class QueryExceptionHandler extends ExceptionHandler<QueryException> {
@override
Response handle(QueryException exception, Request? request) {
return Response.json({
'success': false,
'message': 'Query error occurred',
'error': exception.cause,
}, 500);
}
}

class NotFoundExceptionHandler extends ExceptionHandler<NotFoundException> {
@override
Response handle(NotFoundException exception, Request? request) {
return Response.json({
'success': false,
'message': exception.message,
}, 404);
}
}

class ValidationExceptionHandler extends ExceptionHandler<ValidationException> {
@override
Response handle(ValidationException exception, Request? request) {
return Response.json({
'success': false,
'message': 'Validation failed',
'errors': exception.message,
}, 422);
}
}

class ThirdPartyExceptionHandler extends GeneralExceptionHandler {
@override
Response? handle(dynamic exception, Request? request) {
return Response.json({
'success': false,
'message': 'An unexpected error occurred',
'error': exception.toString(),
}, 500);
}
}

class AppExceptionServiceProvider extends ServiceProvider {
@override
Future<void> boot() async {}

@override
Future<void> register() async {
Application().addExceptionHandlers({
DatabaseException: DatabaseExceptionHandler(),
QueryException: QueryExceptionHandler(),
NotFoundException: NotFoundExceptionHandler(),
ValidationException: ValidationExceptionHandler(),
});
Application().setGeneralExceptionHandler(
ThirdPartyExceptionHandler(),
);
}
}
9 changes: 9 additions & 0 deletions example/main.dart
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
import 'package:vania/vania.dart';
import 'app_exception_handler.dart';

void main() async {
Application().initialize(
config: {
'providers': [AppExceptionServiceProvider()],
},
);
}
24 changes: 24 additions & 0 deletions lib/application.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'src/container.dart';
import 'src/exception/exception_handler.dart';
import 'src/ioc_container.dart';
import 'src/localization_handler/localization.dart';
import 'src/server/base_http_server.dart';
Expand All @@ -10,6 +11,9 @@ import 'src/utils/helper.dart' show env;
class Application extends Container {
static Application? _singleton;

final Map<Type, ExceptionHandler> _exceptionHandlers = {};
GeneralExceptionHandler? _generalExceptionHandler;

factory Application() {
if (_singleton == null) {
_singleton = Application._internal();
Expand All @@ -21,6 +25,26 @@ class Application extends Container {

Application._internal();

void addExceptionHandler<T>(ExceptionHandler<T> handler) {
_exceptionHandlers[T] = handler;
}

void addExceptionHandlers(Map<Type, ExceptionHandler> handlers) {
_exceptionHandlers.addAll(handlers);
}

void setGeneralExceptionHandler(GeneralExceptionHandler handler) {
_generalExceptionHandler = handler;
}

ExceptionHandler? getExceptionHandler(Type type) {
return _exceptionHandlers[type];
}

GeneralExceptionHandler? getGeneralExceptionHandler() {
return _generalExceptionHandler;
}

late BaseHttpServer _server;

Future<void> initialize({
Expand Down
8 changes: 4 additions & 4 deletions lib/src/authentication/authentication.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class Auth {
await PersonalAccessToken().query.insert({
'name': _userGuard,
'tokenable_id': user['id'],
'token': md5.convert(utf8.encode(newToken['access_token'])),
'token': md5.convert(utf8.encode(newToken['access_token'])).toString(),
'created_at': DateTime.now(),
});
}
Expand Down Expand Up @@ -215,7 +215,7 @@ class Auth {
///
Future<bool> deleteCurrentToken(String token) async {
await PersonalAccessToken().query
.where('token', '=', md5.convert(utf8.encode(token)))
.where('token', '=', md5.convert(utf8.encode(token)).toString())
.update({'deleted_at': DateTime.now()});
return true;
}
Expand Down Expand Up @@ -254,7 +254,7 @@ class Auth {
return true;
} else {
Map<String, dynamic>? exists = await PersonalAccessToken().query
.where('token', '=', md5.convert(utf8.encode(token)))
.where('token', '=', md5.convert(utf8.encode(token)).toString())
.whereNull('deleted_at')
.first(['id']);
// Throw 401 Error if token not found
Expand All @@ -263,7 +263,7 @@ class Auth {
}

await PersonalAccessToken().query
.where('token', '=', md5.convert(utf8.encode(token)))
.where('token', '=', md5.convert(utf8.encode(token)).toString())
.update({'last_used_at': DateTime.now()});

if (user == null) {
Expand Down
14 changes: 14 additions & 0 deletions lib/src/exception/exception_handler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import 'package:vania/src/http/request/request.dart';
import 'package:vania/src/http/response/response.dart';

abstract class ExceptionHandler<T> {
const ExceptionHandler();

Response handle(T exception, Request? request);
}

abstract class GeneralExceptionHandler {
const GeneralExceptionHandler();

Response? handle(dynamic exception, Request? request);
}
42 changes: 42 additions & 0 deletions lib/src/http/controller/controller_handler.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import 'package:vania/src/exception/database_exception.dart';
import 'package:vania/src/exception/exception_handler.dart';
import 'package:vania/src/exception/query_exception.dart';
import 'package:vania/src/exception/validation_exception.dart';
import 'package:vania/src/http/request/request.dart';
import 'package:vania/src/http/response/response.dart';
import 'package:vania/src/route/route_data.dart';
import 'package:vania/src/route/route_history.dart';
import 'package:vania/application.dart';

import '../../exception/base_http_exception.dart';
import '../../exception/invalid_argument_exception.dart';
Expand Down Expand Up @@ -51,6 +53,10 @@ class ControllerHandler {

response.makeResponse(request.response);
} on ValidationException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
bool isHtml = request.request.headers
.value('accept')
.toString()
Expand All @@ -63,17 +69,53 @@ class ControllerHandler {
error.response(false).makeResponse(request.response);
}
} on InvalidArgumentException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
_response(request, error.message);
} on DatabaseException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
_response(request, error.message, 500);
} on QueryException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
_response(request, error.cause ?? '', 500);
} on BaseHttpResponseException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
_response(request, error.message, error.code);
} catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(request.response);
}
_response(request, error.toString());
}
}

Response? _handleException(dynamic exception, Request request) {
try {
ExceptionHandler? handler =
Application().getExceptionHandler(exception.runtimeType);
if (handler != null) {
return handler.handle(exception, request);
}
GeneralExceptionHandler? generalHandler =
Application().getGeneralExceptionHandler();
if (generalHandler != null) {
return generalHandler.handle(exception, request);
}
} catch (_) {}
return null;
}
}

void _response(Request req, message, [statusCode = 500]) {
Expand Down
42 changes: 41 additions & 1 deletion lib/src/http/request/request_handler.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:io';
import 'package:vania/src/exception/database_exception.dart';
import 'package:vania/src/exception/exception_handler.dart';
import 'package:vania/src/exception/query_exception.dart';
import 'package:vania/src/extensions/extensions.dart';
import 'package:vania/src/http/response/response.dart';
Expand All @@ -24,6 +25,7 @@ import 'package:vania/src/websocket/web_socket_handler.dart';
import 'package:vania/src/exception/base_http_exception.dart';
import 'package:vania/src/logger/logger.dart';
import 'package:vania/src/utils/helper.dart';
import 'package:vania/application.dart';
import 'request.dart';

HttpRequest? globalHttpRequest;
Expand All @@ -48,6 +50,7 @@ class RequestHandler {
WebSocketHandler().handler(req);
} else {
bool isHtml = req.headers.value('accept').toString().contains('html');
Request? request;
try {
HttpCors(req);
RouteData? route = httpRouteHandler(req);
Expand All @@ -57,7 +60,7 @@ class RequestHandler {
String requestMethod = req.method.toUpperCase();

if (route != null) {
Request request = Request().from(request: req, route: route);
request = Request().from(request: req, route: route);
await request.extractBody();

if (isHtml) {
Expand Down Expand Up @@ -92,6 +95,11 @@ class RequestHandler {
}
}
} on BaseHttpResponseException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(req.response);
}

if (error is NotFoundException && isHtml) {
if (File('lib/resources/view/errors/404.html').existsSync()) {
return view('errors/404').makeResponse(req.response);
Expand Down Expand Up @@ -120,19 +128,51 @@ class RequestHandler {

error.response(isHtml).makeResponse(req.response);
} on InvalidArgumentException catch (e) {
Response? customResponse = _handleException(e, request);
if (customResponse != null) {
return customResponse.makeResponse(req.response);
}
Logger.log(e.message, type: Logger.ERROR);
_response(req, e.message);
} on DatabaseException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(req.response);
}
_response(req, error.message);
} on QueryException catch (error) {
Response? customResponse = _handleException(error, request);
if (customResponse != null) {
return customResponse.makeResponse(req.response);
}
_response(req, error.cause);
} catch (e) {
Response? customResponse = _handleException(e, request);
if (customResponse != null) {
return customResponse.makeResponse(req.response);
}
Logger.log(e.toString(), type: Logger.ERROR);
_response(req, e.toString());
}
}
}

Response? _handleException(dynamic exception, Request? request) {
try {
ExceptionHandler? handler =
Application().getExceptionHandler(exception.runtimeType);
if (handler != null) {
return handler.handle(exception, request);
}
GeneralExceptionHandler? generalHandler =
Application().getGeneralExceptionHandler();
if (generalHandler != null) {
return generalHandler.handle(exception, request);
}
} catch (_) {}
return null;
}

void _response(HttpRequest req, dynamic message, {int statusCode = 500}) {
if (req.headers.value('accept').toString().contains('html')) {
Response.html(message).makeResponse(req.response);
Expand Down
13 changes: 12 additions & 1 deletion lib/vania.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ export 'src/config/config.dart';
export 'src/cryptographic/hash.dart';

export 'src/exception/base_http_exception.dart';
export 'src/exception/database_exception.dart';
export 'src/exception/exception_handler.dart';
export 'src/exception/forbidden_exception.dart';
export 'src/exception/http_exception.dart';
export 'src/exception/internal_server_error.dart';
export 'src/exception/invalid_argument_exception.dart';
export 'src/exception/not_found_exception.dart';
export 'src/exception/page_expired_exception.dart';
export 'src/exception/query_exception.dart';
export 'src/exception/redirect_exception.dart';
export 'src/exception/throttle_exception.dart';
export 'src/exception/validation_exception.dart';
export 'src/exception/unauthenticated.dart';
export 'src/exception/unauthorized_exception.dart';
export 'src/exception/validation_exception.dart';
export 'application.dart';