From 0e5f315773b97e78b15915705d84ad71295344b6 Mon Sep 17 00:00:00 2001 From: thosakwe Date: Wed, 23 Nov 2016 14:50:17 -0500 Subject: [PATCH] Fixed a bunch of issues --- README.md | 2 +- lib/src/http/controller.dart | 25 ++++++++--- lib/src/http/request_context.dart | 37 +++++++++++----- lib/src/http/response_context.dart | 20 ++++++--- lib/src/http/server.dart | 71 +++++++++++++++++++----------- pubspec.yaml | 2 +- test/di.dart | 12 +++-- 7 files changed, 111 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index bb83bbe0..e041d96e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # angel_framework -![version 1.0.0-dev](https://img.shields.io/badge/version-1.0.0--dev.23-red.svg) +![version 1.0.0-dev.24](https://img.shields.io/badge/version-1.0.0--dev.24-red.svg) ![build status](https://travis-ci.org/angel-dart/framework.svg) Core libraries for the Angel Framework. \ No newline at end of file diff --git a/lib/src/http/controller.dart b/lib/src/http/controller.dart index 7eb716ba..fbc37927 100644 --- a/lib/src/http/controller.dart +++ b/lib/src/http/controller.dart @@ -73,19 +73,30 @@ class Controller { var arg = req.params[name]; if (arg == null) { - if (parameter.type.reflectedType != dynamic) { + if (req.injections.containsKey(name)) { + args.add(req.injections[name]); + continue; + } + + final type = parameter.type.reflectedType; + + if (req.injections.containsKey(type)) { + args.add(req.injections[type]); + continue; + } + + if (type != dynamic) { try { - arg = app.container.make(parameter.type.reflectedType); - if (arg != null) { - args.add(arg); - continue; - } + args.add(app.container.make(type)); + continue; } catch (e) { // + print(e); + print(req.injections); } } - if (!exposeMirror.reflectee.allowNull.contain(name)) + if (!exposeMirror.reflectee.allowNull.contains(name)) throw new AngelHttpException.BadRequest( message: "Missing parameter '$name'"); } else diff --git a/lib/src/http/request_context.dart b/lib/src/http/request_context.dart index af2544a3..a759d101 100644 --- a/lib/src/http/request_context.dart +++ b/lib/src/http/request_context.dart @@ -10,26 +10,32 @@ import 'angel_base.dart'; class RequestContext extends Extensible { BodyParseResult _body; ContentType _contentType; + HttpRequest _io; String _path; - HttpRequest _underlyingRequest; /// The [Angel] instance that is responding to this request. AngelBase app; /// Any cookies sent with this request. - List get cookies => underlyingRequest.cookies; + List get cookies => io.cookies; /// All HTTP headers sent with this request. - HttpHeaders get headers => underlyingRequest.headers; + HttpHeaders get headers => io.headers; /// The requested hostname. - String get hostname => underlyingRequest.headers.value(HttpHeaders.HOST); + String get hostname => io.headers.value(HttpHeaders.HOST); + + /// A [Map] of values that should be DI'd. + final Map injections = {}; + + /// The underlying [HttpRequest] instance underneath this context. + HttpRequest get io => _io; /// The user's IP. String get ip => remoteAddress.address; /// This request's HTTP method. - String get method => underlyingRequest.method; + String get method => io.method; /// All post data submitted to the server. Map get body => _body.body; @@ -51,24 +57,27 @@ class RequestContext extends Extensible { /// The remote address requesting this resource. InternetAddress get remoteAddress => - underlyingRequest.connectionInfo.remoteAddress; + io.connectionInfo.remoteAddress; /// The user's HTTP session. - HttpSession get session => underlyingRequest.session; + HttpSession get session => io.session; /// The [Uri] instance representing the path this request is responding to. - Uri get uri => underlyingRequest.uri; + Uri get uri => io.uri; /// Is this an **XMLHttpRequest**? bool get xhr => - underlyingRequest.headers + io.headers .value("X-Requested-With") ?.trim() ?.toLowerCase() == 'xmlhttprequest'; - /// The underlying [HttpRequest] instance underneath this context. - HttpRequest get underlyingRequest => _underlyingRequest; + @deprecated + HttpRequest get underlyingRequest { + throw new Exception( + '`RequestContext#underlyingRequest` is deprecated. Please update your application to use the newer `RequestContext#io`.'); + } /// Magically transforms an [HttpRequest] into a [RequestContext]. static Future from(HttpRequest request, AngelBase app) async { @@ -80,10 +89,14 @@ class RequestContext extends Extensible { .toString() .replaceAll("?" + request.uri.query, "") .replaceAll(new RegExp(r'/+$'), ''); - ctx._underlyingRequest = request; + ctx._io = request; ctx._body = await parseBody(request); return ctx; } + + void inject(Type type, value) { + injections[type] = value; + } } diff --git a/lib/src/http/response_context.dart b/lib/src/http/response_context.dart index 4e9c281b..fe175451 100644 --- a/lib/src/http/response_context.dart +++ b/lib/src/http/response_context.dart @@ -25,16 +25,22 @@ class ResponseContext extends Extensible { /// Sets the status code to be sent with this response. void status(int code) { - underlyingResponse.statusCode = code; + io.statusCode = code; } /// The underlying [HttpResponse] under this instance. - final HttpResponse underlyingResponse; + final HttpResponse io; - ResponseContext(this.underlyingResponse, this.app); + @deprecated + HttpResponse get underlyingRequest { + throw new Exception( + '`ResponseContext#underlyingResponse` is deprecated. Please update your application to use the newer `ResponseContext#io`.'); + } + + ResponseContext(this.io, this.app); /// Any and all cookies to be sent to the user. - List get cookies => underlyingResponse.cookies; + List get cookies => io.cookies; /// Set this to true if you will manually close the response. bool willCloseItself = false; @@ -57,9 +63,9 @@ class ResponseContext extends Extensible { /// Sets a response header to the given value, or retrieves its value. header(String key, [String value]) { if (value == null) - return underlyingResponse.headers[key]; + return io.headers[key]; else - underlyingResponse.headers.set(key, value); + io.headers.set(key, value); } /// Serializes JSON to the response. @@ -163,7 +169,7 @@ class ResponseContext extends Extensible { header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path)); willCloseItself = true; - await file.openRead().pipe(underlyingResponse); + await file.openRead().pipe(io); } /// Writes data to the response. diff --git a/lib/src/http/server.dart b/lib/src/http/server.dart index 72c77ed4..38a258f0 100644 --- a/lib/src/http/server.dart +++ b/lib/src/http/server.dart @@ -105,6 +105,8 @@ class Angel extends AngelBase { /// Loads some base dependencies into the service container. void bootstrapContainer() { container.singleton(this, as: AngelBase); + container.singleton(this, as: Routable); + container.singleton(this, as: Router); container.singleton(this); if (runtimeType != Angel) container.singleton(this, as: Angel); @@ -130,7 +132,7 @@ class Angel extends AngelBase { } if (handler is RawRequestHandler) { - var result = await handler(req.underlyingRequest); + var result = await handler(req.io); if (result is bool) return result == true; else if (result != null) { @@ -167,8 +169,8 @@ class Angel extends AngelBase { } res.willCloseItself = true; - res.underlyingResponse.write(god.serialize(handler)); - await res.underlyingResponse.close(); + res.io.write(god.serialize(handler)); + await res.io.close(); return false; } @@ -177,23 +179,34 @@ class Angel extends AngelBase { final req = await RequestContext.from(request, this); final res = new ResponseContext(request.response, this); - String requestedUrl = request.uri - .path - .replaceAll(_straySlashes, ''); + String requestedUrl = request.uri.path.replaceAll(_straySlashes, ''); if (requestedUrl.isEmpty) requestedUrl = '/'; - final route = resolve(requestedUrl, method: request.method); - _printDebug('Resolved ${requestedUrl} -> $route'); - req.params.addAll(route?.parseParameters(requestedUrl) ?? {}); + final resolved = []; - final handlerSequence = []..addAll(before); - if (route != null) handlerSequence.addAll(route.handlerSequence); - handlerSequence.addAll(after); + if (requestedUrl == '/') { + resolved.add(root.indexRoute); + } else { + resolved.addAll(resolveAll(requestedUrl, method: request.method)); + final route = resolved.first; + req.params.addAll(route?.parseParameters(requestedUrl) ?? {}); + req.inject(Match, route.match(requestedUrl)); + } - _printDebug('Handler sequence on $requestedUrl: $handlerSequence'); + final pipeline = []..addAll(before); - for (final handler in handlerSequence) { + if (resolved.isNotEmpty) { + for (final route in resolved) { + pipeline.addAll(route.handlerSequence); + } + } + + pipeline.addAll(after); + + _printDebug('Handler sequence on $requestedUrl: $pipeline'); + + for (final handler in pipeline) { try { _printDebug('Executing handler: $handler'); final result = await executeHandler(handler, req, res); @@ -249,8 +262,7 @@ class Angel extends AngelBase { // Run a function after injecting from service container Future runContained(Function handler, RequestContext req, ResponseContext res, - {Map namedParameters, - Map injecting}) async { + {Map namedParameters}) async { ClosureMirror closureMirror = reflect(handler); List args = []; @@ -261,9 +273,9 @@ class Angel extends AngelBase { args.add(res); else { // First, search to see if we can map this to a type - if (parameter.type.reflectedType != dynamic) { - args.add(container.make(parameter.type.reflectedType, - namedParameters: namedParameters, injecting: injecting)); + if (req.injections.containsKey(parameter.type.reflectedType)) { + args.add(req.injections[parameter.type.reflectedType]); + continue; } else { String name = MirrorSystem.getName(parameter.simpleName); @@ -273,7 +285,12 @@ class Angel extends AngelBase { args.add(req); else if (name == "res") args.add(res); - else { + else if (req.injections.containsKey(name)) + args.add(req.injections[name]); + else if (parameter.type.reflectedType != dynamic) { + args.add(container.make(parameter.type.reflectedType, + injecting: req.injections)); + } else { throw new Exception( "Cannot resolve parameter '$name' within handler."); } @@ -324,20 +341,22 @@ class Angel extends AngelBase { /// Provide paths to a certificate chain and server key (both .pem). /// If no password is provided, a random one will be generated upon running /// the server. - Angel.secure(String certificateChainPath, String serverKeyPath, - {bool debug: false, String password}) - : super(debug: debug) { - bootstrapContainer(); - _serverGenerator = (InternetAddress address, int port) async { + factory Angel.secure(String certificateChainPath, String serverKeyPath, + {bool debug: false, String password}) { + final app = new Angel(debug: debug); + + app._serverGenerator = (InternetAddress address, int port) async { var certificateChain = Platform.script.resolve(certificateChainPath).toFilePath(); var serverKey = Platform.script.resolve(serverKeyPath).toFilePath(); var serverContext = new SecurityContext(); serverContext.useCertificateChain(certificateChain); serverContext.usePrivateKey(serverKey, - password: password ?? _randomString(8)); + password: password ?? app._randomString(8)); return await HttpServer.bindSecure(address, port, serverContext); }; + + return app; } } diff --git a/pubspec.yaml b/pubspec.yaml index d5703a9d..e22d5e84 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: angel_framework -version: 1.0.0-dev.23 +version: 1.0.0-dev.24 description: Core libraries for the Angel framework. author: Tobe O homepage: https://github.com/angel-dart/angel_framework diff --git a/test/di.dart b/test/di.dart index bfc9d9af..7b8599e9 100644 --- a/test/di.dart +++ b/test/di.dart @@ -22,12 +22,12 @@ main() { app.container.singleton(new Todo(text: TEXT, over: OVER)); app.get("/errands", (Todo singleton) => singleton); - app.get("/errands3", (Errand singleton, Todo foo, RequestContext req) => singleton.text); + app.get("/errands3", + (Errand singleton, Todo foo, RequestContext req) => singleton.text); await app.configure(new SingletonController()); await app.configure(new ErrandController()); server = await app.startServer(); - print('server: $server, httpServer: ${app.httpServer}'); url = "http://${server.address.host}:${server.port}"; }); @@ -77,7 +77,11 @@ class SingletonController extends Controller { @Expose("/errands4") class ErrandController extends Controller { @Expose("/") - errand(Errand errand) => errand.text; + errand(Errand errand, Match match) { + expect(match, isNotNull); + print('Match: ${match.group(0)}'); + return errand.text; + } } class Errand { @@ -85,4 +89,4 @@ class Errand { String get text => todo.text; Errand(this.todo); -} \ No newline at end of file +}