2016-09-15 19:53:01 +00:00
|
|
|
library angel_framework.http.server;
|
|
|
|
|
|
|
|
import 'dart:async';
|
2017-11-28 18:14:50 +00:00
|
|
|
import 'dart:collection' show HashMap;
|
2017-08-15 23:01:16 +00:00
|
|
|
import 'dart:convert';
|
2016-09-15 19:53:01 +00:00
|
|
|
import 'dart:io';
|
2018-08-20 02:31:08 +00:00
|
|
|
|
2018-08-19 15:33:25 +00:00
|
|
|
import 'package:angel_container/angel_container.dart';
|
2017-09-22 04:48:22 +00:00
|
|
|
import 'package:angel_http_exception/angel_http_exception.dart';
|
2017-11-28 18:14:50 +00:00
|
|
|
import 'package:angel_route/angel_route.dart';
|
|
|
|
import 'package:combinator/combinator.dart';
|
2018-08-21 18:50:43 +00:00
|
|
|
import 'package:http_parser/http_parser.dart';
|
2018-12-14 02:31:21 +00:00
|
|
|
import 'package:logging/logging.dart';
|
2018-11-13 18:07:27 +00:00
|
|
|
import 'package:mime/mime.dart';
|
2017-08-28 15:29:27 +00:00
|
|
|
import 'package:tuple/tuple.dart';
|
2018-08-20 02:31:08 +00:00
|
|
|
|
2018-10-22 15:56:03 +00:00
|
|
|
import 'controller.dart';
|
2018-08-20 20:58:37 +00:00
|
|
|
import 'hooked_service.dart';
|
2016-09-15 19:53:01 +00:00
|
|
|
import 'request_context.dart';
|
|
|
|
import 'response_context.dart';
|
|
|
|
import 'routable.dart';
|
|
|
|
import 'service.dart';
|
2016-02-28 13:11:17 +00:00
|
|
|
|
2018-08-21 01:05:05 +00:00
|
|
|
//final RegExp _straySlashes = new RegExp(r'(^/+)|(/+$)');
|
2016-10-22 20:41:36 +00:00
|
|
|
|
2017-02-25 00:16:31 +00:00
|
|
|
/// A function that configures an [Angel] server in some way.
|
2018-06-08 07:06:26 +00:00
|
|
|
typedef FutureOr AngelConfigurer(Angel app);
|
2017-02-25 00:16:31 +00:00
|
|
|
|
2018-08-20 02:55:54 +00:00
|
|
|
/// A function that asynchronously generates a view from the given path and data.
|
|
|
|
typedef FutureOr<String> ViewGenerator(String path,
|
|
|
|
[Map<String, dynamic> data]);
|
|
|
|
|
2016-02-28 13:11:17 +00:00
|
|
|
/// A powerful real-time/REST/MVC server class.
|
2018-08-20 02:55:54 +00:00
|
|
|
class Angel extends Routable {
|
|
|
|
static ViewGenerator noViewEngineConfigured =
|
|
|
|
(String view, [Map data]) => 'No view engine has been configured yet.';
|
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
final List<Angel> _children = [];
|
2018-08-20 20:53:30 +00:00
|
|
|
final Map<String,
|
|
|
|
Tuple3<List, Map<String, dynamic>, ParseResult<Map<String, dynamic>>>>
|
2017-11-28 18:14:50 +00:00
|
|
|
handlerCache = new HashMap();
|
2017-08-15 23:01:16 +00:00
|
|
|
|
2017-04-01 01:00:24 +00:00
|
|
|
Router _flattened;
|
2017-11-28 18:14:50 +00:00
|
|
|
bool _isProduction;
|
2016-12-21 18:18:26 +00:00
|
|
|
Angel _parent;
|
2016-09-17 16:12:25 +00:00
|
|
|
|
2017-08-15 23:01:16 +00:00
|
|
|
/// A global Map of converters that can transform responses bodies.
|
|
|
|
final Map<String, Converter<List<int>, List<int>>> encoders = {};
|
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
final Map<dynamic, InjectionRequest> _preContained = {};
|
2018-02-07 03:48:26 +00:00
|
|
|
|
2018-11-13 18:07:27 +00:00
|
|
|
/// A [MimeTypeResolver] that can be used to specify the MIME types of files not known by `package:mime`.
|
|
|
|
final MimeTypeResolver mimeTypeResolver = new MimeTypeResolver();
|
|
|
|
|
2018-02-07 03:48:26 +00:00
|
|
|
/// A middleware to inject a serialize on every request.
|
2018-02-07 03:54:24 +00:00
|
|
|
String Function(dynamic) serializer;
|
2016-12-31 01:46:41 +00:00
|
|
|
|
2017-06-06 12:42:33 +00:00
|
|
|
/// A [Map] of dependency data obtained via reflection.
|
|
|
|
///
|
|
|
|
/// You may modify this [Map] yourself if you intend to avoid reflection entirely.
|
|
|
|
Map<dynamic, InjectionRequest> get preContained => _preContained;
|
|
|
|
|
2018-02-07 03:51:52 +00:00
|
|
|
/// Returns the [flatten]ed version of this router in production.
|
|
|
|
Router get optimizedRouter => _flattened ?? this;
|
|
|
|
|
2017-03-02 22:06:02 +00:00
|
|
|
/// Determines whether to allow HTTP request method overrides.
|
|
|
|
bool allowMethodOverrides = true;
|
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// All child application mounted on this instance.
|
|
|
|
List<Angel> get children => new List<Angel>.unmodifiable(_children);
|
|
|
|
|
2017-10-04 14:09:12 +00:00
|
|
|
final Map<Pattern, Controller> _controllers = {};
|
|
|
|
|
|
|
|
/// A set of [Controller] objects that have been loaded into the application.
|
|
|
|
Map<Pattern, Controller> get controllers => _controllers;
|
|
|
|
|
2016-12-21 18:18:26 +00:00
|
|
|
/// Indicates whether the application is running in a production environment.
|
|
|
|
///
|
|
|
|
/// The criteria for this is the `ANGEL_ENV` environment variable being set to
|
|
|
|
/// `'production'`.
|
2017-04-01 01:00:24 +00:00
|
|
|
///
|
|
|
|
/// This value is memoized the first time you call it, so do not change environment
|
|
|
|
/// configuration at runtime!
|
|
|
|
bool get isProduction {
|
2017-11-28 18:14:50 +00:00
|
|
|
return _isProduction ??=
|
|
|
|
(Platform.environment['ANGEL_ENV'] == 'production');
|
2017-04-01 01:00:24 +00:00
|
|
|
}
|
2016-12-21 18:18:26 +00:00
|
|
|
|
|
|
|
/// Returns the parent instance of this application, if any.
|
|
|
|
Angel get parent => _parent;
|
|
|
|
|
2017-09-22 04:48:22 +00:00
|
|
|
/// Outputs diagnostics and debug messages.
|
2018-12-14 02:31:21 +00:00
|
|
|
Logger logger;
|
2017-09-22 04:48:22 +00:00
|
|
|
|
2017-02-01 21:43:18 +00:00
|
|
|
/// Plug-ins to be called right before server startup.
|
|
|
|
///
|
|
|
|
/// If the server is never started, they will never be called.
|
2017-09-22 04:48:22 +00:00
|
|
|
final List<AngelConfigurer> startupHooks = [];
|
2017-02-01 21:43:18 +00:00
|
|
|
|
2017-09-22 04:48:22 +00:00
|
|
|
/// Plug-ins to be called right before server shutdown.
|
2017-04-04 08:35:36 +00:00
|
|
|
///
|
|
|
|
/// If the server is never [close]d, they will never be called.
|
2017-09-22 04:48:22 +00:00
|
|
|
final List<AngelConfigurer> shutdownHooks = [];
|
2017-04-04 08:35:36 +00:00
|
|
|
|
2016-12-19 01:38:23 +00:00
|
|
|
/// Always run before responses are sent.
|
|
|
|
///
|
2017-09-22 14:03:23 +00:00
|
|
|
/// These will only not run if a response's `willCloseItself` is set to `true`.
|
2016-12-19 01:38:23 +00:00
|
|
|
final List<RequestHandler> responseFinalizers = [];
|
|
|
|
|
2018-08-20 02:55:54 +00:00
|
|
|
/// A [Map] of application-specific data that can be accessed by any
|
|
|
|
/// piece of code that can see this [Angel] instance.
|
|
|
|
///
|
|
|
|
/// Packages like `package:angel_configuration` populate this map
|
|
|
|
/// for you.
|
|
|
|
final Map configuration = {};
|
|
|
|
|
|
|
|
/// A function that renders views.
|
|
|
|
///
|
|
|
|
/// Called by [ResponseContext]@`render`.
|
|
|
|
ViewGenerator viewGenerator = noViewEngineConfigured;
|
|
|
|
|
2017-03-04 21:12:39 +00:00
|
|
|
/// The handler currently configured to run on [AngelHttpException]s.
|
2017-09-22 04:48:22 +00:00
|
|
|
Function(AngelHttpException e, RequestContext req, ResponseContext res)
|
2017-10-04 14:09:12 +00:00
|
|
|
errorHandler =
|
|
|
|
(AngelHttpException e, RequestContext req, ResponseContext res) {
|
2017-12-06 14:46:35 +00:00
|
|
|
if (!req.accepts('text/html', strict: true) &&
|
2017-10-04 14:09:12 +00:00
|
|
|
(req.accepts('application/json') ||
|
|
|
|
req.accepts('application/javascript'))) {
|
|
|
|
res.json(e.toJson());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-08-21 18:50:43 +00:00
|
|
|
res.contentType = new MediaType('text', 'html', {'charset': 'utf8'});
|
2016-12-19 01:38:23 +00:00
|
|
|
res.statusCode = e.statusCode;
|
2016-07-03 22:23:55 +00:00
|
|
|
res.write("<!DOCTYPE html><html><head><title>${e.message}</title>");
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
res.write("</head><body><h1>${e.message}</h1><ul>");
|
2016-12-31 01:46:41 +00:00
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
for (String error in e.errors) {
|
|
|
|
res.write("<li>$error</li>");
|
|
|
|
}
|
2016-12-31 01:46:41 +00:00
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
res.write("</ul></body></html>");
|
2018-08-21 01:57:26 +00:00
|
|
|
res.close();
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
};
|
|
|
|
|
2017-04-01 01:00:24 +00:00
|
|
|
@override
|
2018-08-20 20:43:38 +00:00
|
|
|
Route<RequestHandler> addRoute(
|
|
|
|
String method, String path, RequestHandler handler,
|
|
|
|
{Iterable<RequestHandler> middleware: const <RequestHandler>[]}) {
|
2017-04-01 01:00:24 +00:00
|
|
|
if (_flattened != null) {
|
2017-09-22 14:53:49 +00:00
|
|
|
logger?.warning(
|
2017-04-02 19:14:10 +00:00
|
|
|
'WARNING: You added a route ($method $path) to the router, after it had been optimized.');
|
2017-09-22 14:53:49 +00:00
|
|
|
logger?.warning(
|
|
|
|
'This route will be ignored, and no requests will ever reach it.');
|
2017-04-01 01:00:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return super.addRoute(method, path, handler, middleware: middleware ?? []);
|
|
|
|
}
|
|
|
|
|
2017-04-02 19:14:10 +00:00
|
|
|
@override
|
2018-08-20 20:43:38 +00:00
|
|
|
mount(String path, Router<RequestHandler> router) {
|
2017-04-02 19:14:10 +00:00
|
|
|
if (_flattened != null) {
|
2017-09-22 14:53:49 +00:00
|
|
|
logger?.warning(
|
2017-04-02 19:14:10 +00:00
|
|
|
'WARNING: You added mounted a child router ($path) on the router, after it had been optimized.');
|
2017-09-22 14:53:49 +00:00
|
|
|
logger?.warning(
|
|
|
|
'This route will be ignored, and no requests will ever reach it.');
|
2017-04-02 19:14:10 +00:00
|
|
|
}
|
2018-08-21 18:50:43 +00:00
|
|
|
|
|
|
|
if (router is Angel) {
|
|
|
|
router._parent = this;
|
|
|
|
_children.add(router);
|
|
|
|
}
|
|
|
|
|
2018-08-20 20:00:18 +00:00
|
|
|
return super.mount(path.toString(), router);
|
2017-04-02 19:14:10 +00:00
|
|
|
}
|
|
|
|
|
2016-09-17 16:12:25 +00:00
|
|
|
/// Loads some base dependencies into the service container.
|
|
|
|
void bootstrapContainer() {
|
2018-08-20 20:00:18 +00:00
|
|
|
if (runtimeType != Angel) {
|
|
|
|
container.registerSingleton(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
container.registerSingleton<Angel>(this);
|
|
|
|
container.registerSingleton<Routable>(this);
|
|
|
|
container.registerSingleton<Router>(this);
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
2017-04-04 08:35:36 +00:00
|
|
|
/// Shuts down the server, and closes any open [StreamController]s.
|
2017-10-28 08:50:16 +00:00
|
|
|
///
|
2018-08-20 02:55:54 +00:00
|
|
|
/// The server will be **COMPLETELY DEFUNCT** after this operation!
|
2018-06-08 07:06:26 +00:00
|
|
|
Future close() {
|
|
|
|
Future.forEach(services.values, (Service service) {
|
|
|
|
service.close();
|
2017-04-04 08:35:36 +00:00
|
|
|
});
|
|
|
|
|
2018-06-08 07:06:26 +00:00
|
|
|
super.close();
|
2018-08-20 02:55:54 +00:00
|
|
|
viewGenerator = noViewEngineConfigured;
|
2017-10-28 08:50:16 +00:00
|
|
|
_preContained.clear();
|
2017-11-18 17:42:31 +00:00
|
|
|
handlerCache.clear();
|
2017-10-28 08:50:16 +00:00
|
|
|
encoders.clear();
|
2018-08-19 15:49:33 +00:00
|
|
|
//_serializer = json.encode;
|
2017-10-28 08:50:16 +00:00
|
|
|
_children.clear();
|
|
|
|
_parent = null;
|
|
|
|
logger = null;
|
|
|
|
startupHooks.clear();
|
|
|
|
shutdownHooks.clear();
|
|
|
|
responseFinalizers.clear();
|
|
|
|
_flattened = null;
|
2018-10-22 15:56:03 +00:00
|
|
|
return new Future.value();
|
2017-04-04 08:35:36 +00:00
|
|
|
}
|
|
|
|
|
2017-04-01 01:00:24 +00:00
|
|
|
@override
|
2017-08-28 15:29:27 +00:00
|
|
|
void dumpTree(
|
|
|
|
{callback(String tree),
|
|
|
|
String header: 'Dumping route tree:',
|
|
|
|
String tab: ' ',
|
|
|
|
bool showMatchers: false}) {
|
2017-04-01 01:00:24 +00:00
|
|
|
if (isProduction) {
|
2017-11-28 18:14:50 +00:00
|
|
|
_flattened ??= flatten(this);
|
2017-04-01 01:00:24 +00:00
|
|
|
|
|
|
|
_flattened.dumpTree(
|
|
|
|
callback: callback,
|
|
|
|
header: header?.isNotEmpty == true
|
|
|
|
? header
|
|
|
|
: (isProduction
|
2017-08-28 15:29:27 +00:00
|
|
|
? 'Dumping flattened route tree:'
|
|
|
|
: 'Dumping route tree:'),
|
2017-11-28 18:14:50 +00:00
|
|
|
tab: tab ?? ' ');
|
2017-04-01 01:00:24 +00:00
|
|
|
} else {
|
|
|
|
super.dumpTree(
|
|
|
|
callback: callback,
|
|
|
|
header: header?.isNotEmpty == true
|
|
|
|
? header
|
|
|
|
: (isProduction
|
2017-08-28 15:29:27 +00:00
|
|
|
? 'Dumping flattened route tree:'
|
|
|
|
: 'Dumping route tree:'),
|
2017-11-28 18:14:50 +00:00
|
|
|
tab: tab ?? ' ');
|
2017-04-01 01:00:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-08 07:06:26 +00:00
|
|
|
Future getHandlerResult(handler, RequestContext req, ResponseContext res) {
|
2016-04-21 20:37:02 +00:00
|
|
|
if (handler is RequestHandler) {
|
2018-06-08 07:06:26 +00:00
|
|
|
var result = handler(req, res);
|
|
|
|
return getHandlerResult(result, req, res);
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handler is Future) {
|
2018-06-08 07:06:26 +00:00
|
|
|
return handler.then((result) => getHandlerResult(result, req, res));
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handler is Function) {
|
2018-06-08 07:06:26 +00:00
|
|
|
var result = runContained(handler, req, res);
|
|
|
|
return getHandlerResult(result, req, res);
|
2017-06-30 13:49:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (handler is Stream) {
|
2018-06-08 07:06:26 +00:00
|
|
|
return getHandlerResult(handler.toList(), req, res);
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
2018-06-08 07:06:26 +00:00
|
|
|
return new Future.value(handler);
|
2017-04-15 17:42:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Runs some [handler]. Returns `true` if request execution should continue.
|
2017-08-28 15:29:27 +00:00
|
|
|
Future<bool> executeHandler(
|
2018-06-08 07:06:26 +00:00
|
|
|
handler, RequestContext req, ResponseContext res) {
|
|
|
|
return getHandlerResult(handler, req, res).then((result) {
|
|
|
|
if (result == null)
|
|
|
|
return false;
|
|
|
|
else if (result is bool) {
|
|
|
|
return result;
|
|
|
|
} else if (result != null) {
|
2018-08-20 02:31:08 +00:00
|
|
|
return res.serialize(result);
|
2018-06-08 07:06:26 +00:00
|
|
|
} else
|
|
|
|
return res.isOpen;
|
|
|
|
});
|
2016-04-18 03:27:23 +00:00
|
|
|
}
|
|
|
|
|
2017-06-06 12:42:33 +00:00
|
|
|
/// Attempts to find a property by the given name within this application.
|
|
|
|
findProperty(key) {
|
2017-09-24 19:43:14 +00:00
|
|
|
if (configuration.containsKey(key)) return configuration[key];
|
2017-06-06 12:42:33 +00:00
|
|
|
return parent != null ? parent.findProperty(key) : null;
|
|
|
|
}
|
|
|
|
|
2017-04-01 01:00:24 +00:00
|
|
|
/// Runs several optimizations, *if* [isProduction] is `true`.
|
|
|
|
///
|
|
|
|
/// * Preprocesses all dependency injection, and eliminates the burden of reflecting handlers
|
2016-12-31 02:00:52 +00:00
|
|
|
/// at run-time.
|
2017-04-01 01:00:24 +00:00
|
|
|
/// * [flatten]s the route tree into a linear one.
|
2017-06-06 12:42:33 +00:00
|
|
|
///
|
|
|
|
/// You may [force] the optimization to run, if you are not running in production.
|
|
|
|
void optimizeForProduction({bool force: false}) {
|
|
|
|
if (isProduction == true || force == true) {
|
2017-06-30 13:49:45 +00:00
|
|
|
_isProduction = true;
|
2017-11-28 18:14:50 +00:00
|
|
|
_flattened ??= flatten(this);
|
2018-12-14 02:31:21 +00:00
|
|
|
logger?.info('Angel is running in production mode.');
|
2016-12-31 02:00:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-31 01:46:41 +00:00
|
|
|
/// Run a function after injecting from service container.
|
|
|
|
/// If this function has been reflected before, then
|
|
|
|
/// the execution will be faster, as the injection requirements were stored beforehand.
|
2018-10-21 08:00:39 +00:00
|
|
|
Future runContained(Function handler, RequestContext req, ResponseContext res,
|
|
|
|
[Container container]) {
|
2018-06-08 07:06:26 +00:00
|
|
|
return new Future.sync(() {
|
|
|
|
if (_preContained.containsKey(handler)) {
|
2018-10-21 08:00:39 +00:00
|
|
|
return handleContained(handler, _preContained[handler], container)(
|
|
|
|
req, res);
|
2018-06-08 07:06:26 +00:00
|
|
|
}
|
2016-12-31 01:46:41 +00:00
|
|
|
|
2018-10-21 08:00:39 +00:00
|
|
|
return runReflected(handler, req, res, container);
|
2018-06-08 07:06:26 +00:00
|
|
|
});
|
2016-12-31 01:46:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Runs with DI, and *always* reflects. Prefer [runContained].
|
2018-10-21 08:00:39 +00:00
|
|
|
Future runReflected(Function handler, RequestContext req, ResponseContext res,
|
|
|
|
[Container container]) {
|
2018-10-21 08:08:08 +00:00
|
|
|
container ??= req?.container ?? res?.app?.container;
|
2018-08-21 14:22:41 +00:00
|
|
|
var h = handleContained(
|
|
|
|
handler,
|
2018-10-21 08:08:08 +00:00
|
|
|
_preContained[handler] = preInject(handler, container.reflector),
|
2018-10-21 08:00:39 +00:00
|
|
|
container);
|
2018-06-08 07:06:26 +00:00
|
|
|
return new Future.sync(() => h(req, res));
|
|
|
|
// return closureMirror.apply(args).reflectee;
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
|
|
|
|
2016-04-18 03:27:23 +00:00
|
|
|
/// Applies an [AngelConfigurer] to this instance.
|
2018-06-08 07:06:26 +00:00
|
|
|
Future configure(AngelConfigurer configurer) {
|
|
|
|
return new Future.sync(() => configurer(this));
|
2016-02-28 13:11:17 +00:00
|
|
|
}
|
|
|
|
|
2018-11-08 02:50:04 +00:00
|
|
|
/// Shorthand for using the [container] to instantiate, and then mount a [Controller].
|
|
|
|
///
|
|
|
|
/// Just like [Container].make, in contexts without properly-reified generics (dev releases of Dart 2),
|
|
|
|
/// provide a [type] argument as well.
|
|
|
|
///
|
|
|
|
/// If you are on `Dart >=2.0.0`, simply call `mountController<T>()`..
|
|
|
|
Future mountController<T extends Controller>([Type type]) {
|
|
|
|
return configure(container.make<T>(type).configureServer);
|
|
|
|
}
|
|
|
|
|
2018-08-20 20:58:37 +00:00
|
|
|
/// Shorthand for calling `all('*', handler)`.
|
|
|
|
Route<RequestHandler> fallback(RequestHandler handler) {
|
|
|
|
return all('*', handler);
|
|
|
|
}
|
|
|
|
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
@override
|
2018-09-11 20:25:07 +00:00
|
|
|
HookedService<Id, Data, T> use<Id, Data, T extends Service<Id, Data>>(
|
|
|
|
String path, T service) {
|
2018-08-21 00:53:44 +00:00
|
|
|
service.app = this;
|
|
|
|
return super.use(path, service)..app = this;
|
Angel.secure, fallback routes, 404, app.addRoute, app.all, services are a go (just missing params, i.e. $sort?), now have service.app, app.before, app.after, angel.configure now uses futures, errors are implemented
2016-04-29 00:01:58 +00:00
|
|
|
}
|
2016-04-22 01:42:39 +00:00
|
|
|
|
2018-08-21 01:05:05 +00:00
|
|
|
Angel(
|
2018-08-21 14:22:41 +00:00
|
|
|
{Reflector reflector: const EmptyReflector(),
|
2018-12-14 02:31:21 +00:00
|
|
|
this.logger,
|
2018-08-21 01:05:05 +00:00
|
|
|
this.allowMethodOverrides: true,
|
|
|
|
this.serializer,
|
2018-08-21 02:44:32 +00:00
|
|
|
this.viewGenerator})
|
|
|
|
: super(reflector) {
|
2016-09-17 16:12:25 +00:00
|
|
|
bootstrapContainer();
|
2018-08-21 01:05:05 +00:00
|
|
|
viewGenerator ??= noViewEngineConfigured;
|
|
|
|
serializer ??= json.encode;
|
2016-09-17 16:12:25 +00:00
|
|
|
}
|
2017-11-28 18:14:50 +00:00
|
|
|
}
|