Fixed a bunch of issues
This commit is contained in:
parent
fc13db72ff
commit
0e5f315773
7 changed files with 111 additions and 58 deletions
|
@ -1,6 +1,6 @@
|
||||||
# angel_framework
|
# angel_framework
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
Core libraries for the Angel Framework.
|
Core libraries for the Angel Framework.
|
|
@ -73,19 +73,30 @@ class Controller {
|
||||||
var arg = req.params[name];
|
var arg = req.params[name];
|
||||||
|
|
||||||
if (arg == null) {
|
if (arg == null) {
|
||||||
if (parameter.type.reflectedType != dynamic) {
|
if (req.injections.containsKey(name)) {
|
||||||
try {
|
args.add(req.injections[name]);
|
||||||
arg = app.container.make(parameter.type.reflectedType);
|
|
||||||
if (arg != null) {
|
|
||||||
args.add(arg);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final type = parameter.type.reflectedType;
|
||||||
|
|
||||||
|
if (req.injections.containsKey(type)) {
|
||||||
|
args.add(req.injections[type]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type != dynamic) {
|
||||||
|
try {
|
||||||
|
args.add(app.container.make(type));
|
||||||
|
continue;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//
|
//
|
||||||
|
print(e);
|
||||||
|
print(req.injections);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!exposeMirror.reflectee.allowNull.contain(name))
|
if (!exposeMirror.reflectee.allowNull.contains(name))
|
||||||
throw new AngelHttpException.BadRequest(
|
throw new AngelHttpException.BadRequest(
|
||||||
message: "Missing parameter '$name'");
|
message: "Missing parameter '$name'");
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -10,26 +10,32 @@ import 'angel_base.dart';
|
||||||
class RequestContext extends Extensible {
|
class RequestContext extends Extensible {
|
||||||
BodyParseResult _body;
|
BodyParseResult _body;
|
||||||
ContentType _contentType;
|
ContentType _contentType;
|
||||||
|
HttpRequest _io;
|
||||||
String _path;
|
String _path;
|
||||||
HttpRequest _underlyingRequest;
|
|
||||||
|
|
||||||
/// The [Angel] instance that is responding to this request.
|
/// The [Angel] instance that is responding to this request.
|
||||||
AngelBase app;
|
AngelBase app;
|
||||||
|
|
||||||
/// Any cookies sent with this request.
|
/// Any cookies sent with this request.
|
||||||
List<Cookie> get cookies => underlyingRequest.cookies;
|
List<Cookie> get cookies => io.cookies;
|
||||||
|
|
||||||
/// All HTTP headers sent with this request.
|
/// All HTTP headers sent with this request.
|
||||||
HttpHeaders get headers => underlyingRequest.headers;
|
HttpHeaders get headers => io.headers;
|
||||||
|
|
||||||
/// The requested hostname.
|
/// 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.
|
/// The user's IP.
|
||||||
String get ip => remoteAddress.address;
|
String get ip => remoteAddress.address;
|
||||||
|
|
||||||
/// This request's HTTP method.
|
/// This request's HTTP method.
|
||||||
String get method => underlyingRequest.method;
|
String get method => io.method;
|
||||||
|
|
||||||
/// All post data submitted to the server.
|
/// All post data submitted to the server.
|
||||||
Map get body => _body.body;
|
Map get body => _body.body;
|
||||||
|
@ -51,24 +57,27 @@ class RequestContext extends Extensible {
|
||||||
|
|
||||||
/// The remote address requesting this resource.
|
/// The remote address requesting this resource.
|
||||||
InternetAddress get remoteAddress =>
|
InternetAddress get remoteAddress =>
|
||||||
underlyingRequest.connectionInfo.remoteAddress;
|
io.connectionInfo.remoteAddress;
|
||||||
|
|
||||||
/// The user's HTTP session.
|
/// 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.
|
/// 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**?
|
/// Is this an **XMLHttpRequest**?
|
||||||
bool get xhr =>
|
bool get xhr =>
|
||||||
underlyingRequest.headers
|
io.headers
|
||||||
.value("X-Requested-With")
|
.value("X-Requested-With")
|
||||||
?.trim()
|
?.trim()
|
||||||
?.toLowerCase() ==
|
?.toLowerCase() ==
|
||||||
'xmlhttprequest';
|
'xmlhttprequest';
|
||||||
|
|
||||||
/// The underlying [HttpRequest] instance underneath this context.
|
@deprecated
|
||||||
HttpRequest get underlyingRequest => _underlyingRequest;
|
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].
|
/// Magically transforms an [HttpRequest] into a [RequestContext].
|
||||||
static Future<RequestContext> from(HttpRequest request, AngelBase app) async {
|
static Future<RequestContext> from(HttpRequest request, AngelBase app) async {
|
||||||
|
@ -80,10 +89,14 @@ class RequestContext extends Extensible {
|
||||||
.toString()
|
.toString()
|
||||||
.replaceAll("?" + request.uri.query, "")
|
.replaceAll("?" + request.uri.query, "")
|
||||||
.replaceAll(new RegExp(r'/+$'), '');
|
.replaceAll(new RegExp(r'/+$'), '');
|
||||||
ctx._underlyingRequest = request;
|
ctx._io = request;
|
||||||
|
|
||||||
ctx._body = await parseBody(request);
|
ctx._body = await parseBody(request);
|
||||||
|
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void inject(Type type, value) {
|
||||||
|
injections[type] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,22 @@ class ResponseContext extends Extensible {
|
||||||
|
|
||||||
/// Sets the status code to be sent with this response.
|
/// Sets the status code to be sent with this response.
|
||||||
void status(int code) {
|
void status(int code) {
|
||||||
underlyingResponse.statusCode = code;
|
io.statusCode = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The underlying [HttpResponse] under this instance.
|
/// 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.
|
/// Any and all cookies to be sent to the user.
|
||||||
List<Cookie> get cookies => underlyingResponse.cookies;
|
List<Cookie> get cookies => io.cookies;
|
||||||
|
|
||||||
/// Set this to true if you will manually close the response.
|
/// Set this to true if you will manually close the response.
|
||||||
bool willCloseItself = false;
|
bool willCloseItself = false;
|
||||||
|
@ -57,9 +63,9 @@ class ResponseContext extends Extensible {
|
||||||
/// Sets a response header to the given value, or retrieves its value.
|
/// Sets a response header to the given value, or retrieves its value.
|
||||||
header(String key, [String value]) {
|
header(String key, [String value]) {
|
||||||
if (value == null)
|
if (value == null)
|
||||||
return underlyingResponse.headers[key];
|
return io.headers[key];
|
||||||
else
|
else
|
||||||
underlyingResponse.headers.set(key, value);
|
io.headers.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes JSON to the response.
|
/// Serializes JSON to the response.
|
||||||
|
@ -163,7 +169,7 @@ class ResponseContext extends Extensible {
|
||||||
|
|
||||||
header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path));
|
header(HttpHeaders.CONTENT_TYPE, lookupMimeType(file.path));
|
||||||
willCloseItself = true;
|
willCloseItself = true;
|
||||||
await file.openRead().pipe(underlyingResponse);
|
await file.openRead().pipe(io);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Writes data to the response.
|
/// Writes data to the response.
|
||||||
|
|
|
@ -105,6 +105,8 @@ class Angel extends AngelBase {
|
||||||
/// Loads some base dependencies into the service container.
|
/// Loads some base dependencies into the service container.
|
||||||
void bootstrapContainer() {
|
void bootstrapContainer() {
|
||||||
container.singleton(this, as: AngelBase);
|
container.singleton(this, as: AngelBase);
|
||||||
|
container.singleton(this, as: Routable);
|
||||||
|
container.singleton(this, as: Router);
|
||||||
container.singleton(this);
|
container.singleton(this);
|
||||||
|
|
||||||
if (runtimeType != Angel) container.singleton(this, as: Angel);
|
if (runtimeType != Angel) container.singleton(this, as: Angel);
|
||||||
|
@ -130,7 +132,7 @@ class Angel extends AngelBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler is RawRequestHandler) {
|
if (handler is RawRequestHandler) {
|
||||||
var result = await handler(req.underlyingRequest);
|
var result = await handler(req.io);
|
||||||
if (result is bool)
|
if (result is bool)
|
||||||
return result == true;
|
return result == true;
|
||||||
else if (result != null) {
|
else if (result != null) {
|
||||||
|
@ -167,8 +169,8 @@ class Angel extends AngelBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
res.willCloseItself = true;
|
res.willCloseItself = true;
|
||||||
res.underlyingResponse.write(god.serialize(handler));
|
res.io.write(god.serialize(handler));
|
||||||
await res.underlyingResponse.close();
|
await res.io.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,23 +179,34 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
final req = await RequestContext.from(request, this);
|
final req = await RequestContext.from(request, this);
|
||||||
final res = new ResponseContext(request.response, this);
|
final res = new ResponseContext(request.response, this);
|
||||||
String requestedUrl = request.uri
|
String requestedUrl = request.uri.path.replaceAll(_straySlashes, '');
|
||||||
.path
|
|
||||||
.replaceAll(_straySlashes, '');
|
|
||||||
|
|
||||||
if (requestedUrl.isEmpty) requestedUrl = '/';
|
if (requestedUrl.isEmpty) requestedUrl = '/';
|
||||||
|
|
||||||
final route = resolve(requestedUrl, method: request.method);
|
final resolved = [];
|
||||||
_printDebug('Resolved ${requestedUrl} -> $route');
|
|
||||||
|
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.params.addAll(route?.parseParameters(requestedUrl) ?? {});
|
||||||
|
req.inject(Match, route.match(requestedUrl));
|
||||||
|
}
|
||||||
|
|
||||||
final handlerSequence = []..addAll(before);
|
final pipeline = []..addAll(before);
|
||||||
if (route != null) handlerSequence.addAll(route.handlerSequence);
|
|
||||||
handlerSequence.addAll(after);
|
|
||||||
|
|
||||||
_printDebug('Handler sequence on $requestedUrl: $handlerSequence');
|
if (resolved.isNotEmpty) {
|
||||||
|
for (final route in resolved) {
|
||||||
|
pipeline.addAll(route.handlerSequence);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (final handler in handlerSequence) {
|
pipeline.addAll(after);
|
||||||
|
|
||||||
|
_printDebug('Handler sequence on $requestedUrl: $pipeline');
|
||||||
|
|
||||||
|
for (final handler in pipeline) {
|
||||||
try {
|
try {
|
||||||
_printDebug('Executing handler: $handler');
|
_printDebug('Executing handler: $handler');
|
||||||
final result = await executeHandler(handler, req, res);
|
final result = await executeHandler(handler, req, res);
|
||||||
|
@ -249,8 +262,7 @@ class Angel extends AngelBase {
|
||||||
|
|
||||||
// Run a function after injecting from service container
|
// Run a function after injecting from service container
|
||||||
Future runContained(Function handler, RequestContext req, ResponseContext res,
|
Future runContained(Function handler, RequestContext req, ResponseContext res,
|
||||||
{Map<String, dynamic> namedParameters,
|
{Map<String, dynamic> namedParameters}) async {
|
||||||
Map<Type, dynamic> injecting}) async {
|
|
||||||
ClosureMirror closureMirror = reflect(handler);
|
ClosureMirror closureMirror = reflect(handler);
|
||||||
List args = [];
|
List args = [];
|
||||||
|
|
||||||
|
@ -261,9 +273,9 @@ class Angel extends AngelBase {
|
||||||
args.add(res);
|
args.add(res);
|
||||||
else {
|
else {
|
||||||
// First, search to see if we can map this to a type
|
// First, search to see if we can map this to a type
|
||||||
if (parameter.type.reflectedType != dynamic) {
|
if (req.injections.containsKey(parameter.type.reflectedType)) {
|
||||||
args.add(container.make(parameter.type.reflectedType,
|
args.add(req.injections[parameter.type.reflectedType]);
|
||||||
namedParameters: namedParameters, injecting: injecting));
|
continue;
|
||||||
} else {
|
} else {
|
||||||
String name = MirrorSystem.getName(parameter.simpleName);
|
String name = MirrorSystem.getName(parameter.simpleName);
|
||||||
|
|
||||||
|
@ -273,7 +285,12 @@ class Angel extends AngelBase {
|
||||||
args.add(req);
|
args.add(req);
|
||||||
else if (name == "res")
|
else if (name == "res")
|
||||||
args.add(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(
|
throw new Exception(
|
||||||
"Cannot resolve parameter '$name' within handler.");
|
"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).
|
/// Provide paths to a certificate chain and server key (both .pem).
|
||||||
/// If no password is provided, a random one will be generated upon running
|
/// If no password is provided, a random one will be generated upon running
|
||||||
/// the server.
|
/// the server.
|
||||||
Angel.secure(String certificateChainPath, String serverKeyPath,
|
factory Angel.secure(String certificateChainPath, String serverKeyPath,
|
||||||
{bool debug: false, String password})
|
{bool debug: false, String password}) {
|
||||||
: super(debug: debug) {
|
final app = new Angel(debug: debug);
|
||||||
bootstrapContainer();
|
|
||||||
_serverGenerator = (InternetAddress address, int port) async {
|
app._serverGenerator = (InternetAddress address, int port) async {
|
||||||
var certificateChain =
|
var certificateChain =
|
||||||
Platform.script.resolve(certificateChainPath).toFilePath();
|
Platform.script.resolve(certificateChainPath).toFilePath();
|
||||||
var serverKey = Platform.script.resolve(serverKeyPath).toFilePath();
|
var serverKey = Platform.script.resolve(serverKeyPath).toFilePath();
|
||||||
var serverContext = new SecurityContext();
|
var serverContext = new SecurityContext();
|
||||||
serverContext.useCertificateChain(certificateChain);
|
serverContext.useCertificateChain(certificateChain);
|
||||||
serverContext.usePrivateKey(serverKey,
|
serverContext.usePrivateKey(serverKey,
|
||||||
password: password ?? _randomString(8));
|
password: password ?? app._randomString(8));
|
||||||
|
|
||||||
return await HttpServer.bindSecure(address, port, serverContext);
|
return await HttpServer.bindSecure(address, port, serverContext);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return app;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: angel_framework
|
name: angel_framework
|
||||||
version: 1.0.0-dev.23
|
version: 1.0.0-dev.24
|
||||||
description: Core libraries for the Angel framework.
|
description: Core libraries for the Angel framework.
|
||||||
author: Tobe O <thosakwe@gmail.com>
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
homepage: https://github.com/angel-dart/angel_framework
|
homepage: https://github.com/angel-dart/angel_framework
|
||||||
|
|
10
test/di.dart
10
test/di.dart
|
@ -22,12 +22,12 @@ main() {
|
||||||
app.container.singleton(new Todo(text: TEXT, over: OVER));
|
app.container.singleton(new Todo(text: TEXT, over: OVER));
|
||||||
|
|
||||||
app.get("/errands", (Todo singleton) => singleton);
|
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 SingletonController());
|
||||||
await app.configure(new ErrandController());
|
await app.configure(new ErrandController());
|
||||||
|
|
||||||
server = await app.startServer();
|
server = await app.startServer();
|
||||||
print('server: $server, httpServer: ${app.httpServer}');
|
|
||||||
url = "http://${server.address.host}:${server.port}";
|
url = "http://${server.address.host}:${server.port}";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,7 +77,11 @@ class SingletonController extends Controller {
|
||||||
@Expose("/errands4")
|
@Expose("/errands4")
|
||||||
class ErrandController extends Controller {
|
class ErrandController extends Controller {
|
||||||
@Expose("/")
|
@Expose("/")
|
||||||
errand(Errand errand) => errand.text;
|
errand(Errand errand, Match match) {
|
||||||
|
expect(match, isNotNull);
|
||||||
|
print('Match: ${match.group(0)}');
|
||||||
|
return errand.text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Errand {
|
class Errand {
|
||||||
|
|
Loading…
Reference in a new issue