platform/lib/src/http/controller.dart

120 lines
4 KiB
Dart
Raw Normal View History

library angel_framework.http.controller;
import 'dart:async';
import 'dart:mirrors';
2016-10-22 20:41:36 +00:00
import 'package:angel_route/angel_route.dart';
import 'package:meta/meta.dart';
import 'metadata.dart';
import 'request_context.dart';
import 'response_context.dart';
import 'routable.dart';
2017-09-24 19:43:14 +00:00
import 'server.dart' show Angel;
2016-06-27 00:20:42 +00:00
2016-12-31 01:46:41 +00:00
/// Supports grouping routes with shared functionality.
2016-06-27 00:20:42 +00:00
class Controller {
2016-12-31 01:46:41 +00:00
Angel _app;
2017-03-28 23:29:22 +00:00
/// The [Angel] application powering this controller.
2016-12-31 01:46:41 +00:00
Angel get app => _app;
2017-03-28 23:29:22 +00:00
2016-12-31 01:46:41 +00:00
final bool debug;
2017-03-28 23:29:22 +00:00
/// If `true` (default), this class will inject itself as a singleton into the [app]'s container when bootstrapped.
final bool injectSingleton;
/// Middleware to run before all handlers in this class.
2016-06-27 00:20:42 +00:00
List middleware = [];
2017-03-28 23:29:22 +00:00
/// A mapping of route paths to routes, produced from the [Expose] annotations on this class.
Map<String, Route> routeMappings = {};
2016-06-27 00:20:42 +00:00
2017-03-28 23:29:22 +00:00
Controller({this.debug: false, this.injectSingleton: true});
2016-12-31 01:46:41 +00:00
@mustCallSuper
2016-12-31 01:46:41 +00:00
Future call(Angel app) async {
2017-03-28 23:29:22 +00:00
_app = app;
if (injectSingleton != false) _app.container.singleton(this);
2016-10-22 20:41:36 +00:00
2016-06-27 00:20:42 +00:00
// Load global expose decl
ClassMirror classMirror = reflectClass(this.runtimeType);
2016-12-31 02:00:52 +00:00
Expose exposeDecl = findExpose();
2016-06-27 00:20:42 +00:00
2016-10-22 20:41:36 +00:00
if (exposeDecl == null) {
2016-06-27 00:20:42 +00:00
throw new Exception(
"All controllers must carry an @Expose() declaration.");
2016-10-22 20:41:36 +00:00
}
2016-12-31 01:46:41 +00:00
var routable = new Routable(debug: debug);
2016-11-28 21:48:00 +00:00
app.use(exposeDecl.path, routable);
2016-11-23 09:10:47 +00:00
TypeMirror typeMirror = reflectType(this.runtimeType);
2016-12-31 01:46:41 +00:00
String name = exposeDecl.as?.isNotEmpty == true
? exposeDecl.as
: MirrorSystem.getName(typeMirror.simpleName);
2016-11-23 09:10:47 +00:00
app.controllers[name] = this;
2016-10-22 20:41:36 +00:00
2016-12-31 01:46:41 +00:00
// Pre-reflect methods
InstanceMirror instanceMirror = reflect(this);
final handlers = []..addAll(exposeDecl.middleware)..addAll(middleware);
final routeBuilder = _routeBuilder(instanceMirror, routable, handlers);
classMirror.instanceMembers.forEach(routeBuilder);
configureRoutes(routable);
}
2016-11-23 09:10:47 +00:00
2016-12-31 01:46:41 +00:00
Function _routeBuilder(
InstanceMirror instanceMirror, Routable routable, List handlers) {
return (Symbol methodName, MethodMirror method) {
if (method.isRegularMethod &&
methodName != #toString &&
methodName != #noSuchMethod &&
methodName != #call &&
methodName != #equals &&
methodName != #==) {
Expose exposeDecl = method.metadata
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null);
if (exposeDecl == null) return;
var reflectedMethod = instanceMirror.getField(methodName).reflectee;
var middleware = []..addAll(handlers)..addAll(exposeDecl.middleware);
2017-01-12 01:52:06 +00:00
String name = exposeDecl.as?.isNotEmpty == true
? exposeDecl.as
: MirrorSystem.getName(methodName);
2016-12-31 01:46:41 +00:00
// Check if normal
if (method.parameters.length == 2 &&
method.parameters[0].type.reflectedType == RequestContext &&
method.parameters[1].type.reflectedType == ResponseContext) {
// Create a regular route
2017-01-12 01:52:06 +00:00
routeMappings[name] = routable
.addRoute(exposeDecl.method, exposeDecl.path, (req, res) async {
var result = await reflectedMethod(req, res);
return result is RequestHandler ? await result(req, res) : result;
}, middleware: middleware);
2016-12-31 01:46:41 +00:00
return;
}
2016-06-27 00:20:42 +00:00
2017-01-15 19:52:14 +00:00
var injection = preInject(reflectedMethod);
if (exposeDecl?.allowNull?.isNotEmpty == true)
injection.optional?.addAll(exposeDecl.allowNull);
routeMappings[name] = routable.addRoute(exposeDecl.method,
exposeDecl.path, handleContained(reflectedMethod, injection),
2016-12-31 01:46:41 +00:00
middleware: middleware);
2016-06-27 00:20:42 +00:00
}
2016-11-23 09:10:47 +00:00
};
}
2016-12-31 01:46:41 +00:00
/// Used to add additional routes to the router from within a [Controller].
void configureRoutes(Routable routable) {}
2016-12-31 02:00:52 +00:00
/// Finds the [Expose] declaration for this class.
Expose findExpose() => reflectClass(runtimeType)
.metadata
.map((m) => m.reflectee)
.firstWhere((r) => r is Expose, orElse: () => null);
2016-12-31 01:46:41 +00:00
}