platform/lib/src/http/routable.dart

157 lines
5.4 KiB
Dart
Raw Normal View History

2016-02-28 13:11:17 +00:00
part of angel_framework.http;
typedef Route RouteAssigner(Pattern path, handler, {List middleware});
2016-04-18 03:27:23 +00:00
_matchingAnnotation(List<InstanceMirror> metadata, Type T) {
for (InstanceMirror metaDatum in metadata) {
if (metaDatum.hasReflectee) {
var reflectee = metaDatum.reflectee;
if (reflectee.runtimeType == T) {
return reflectee;
}
}
}
return null;
}
_getAnnotation(obj, Type T) {
if (obj is Function || obj is Future) {
MethodMirror methodMirror = (reflect(obj) as ClosureMirror).function;
return _matchingAnnotation(methodMirror.metadata, T);
} else {
ClassMirror classMirror = reflectClass(obj.runtimeType);
return _matchingAnnotation(classMirror.metadata, T);
}
return null;
}
2016-02-28 13:11:17 +00:00
/// A routable server that can handle dynamic requests.
class Routable extends Extensible {
2016-02-28 13:11:17 +00:00
/// Additional filters to be run on designated requests.
Map <String, RequestMiddleware> requestMiddleware = {};
2016-02-28 13:11:17 +00:00
/// Dynamic request paths that this server will respond to.
2016-04-18 03:27:23 +00:00
List<Route> routes = [];
/// A set of [Service] objects that have been mapped into routes.
Map <Pattern, Service> services = {};
/// Assigns a middleware to a name for convenience.
registerMiddleware(String name, RequestMiddleware middleware) {
this.requestMiddleware[name] = middleware;
2016-04-18 03:27:23 +00:00
}
/// Retrieves the service assigned to the given path.
Service service(Pattern path) => services[path];
/// Incorporates another [Routable]'s routes into this one's.
///
/// If `hooked` is set to `true` and a [Service] is provided,
/// then that service will be wired to a [HookedService] proxy.
/// If a `middlewareNamespace` is provided, then any middleware
/// from the provided [Routable] will be prefixed by that namespace,
/// with a dot.
/// For example, if the [Routable] has a middleware 'y', and the `middlewareNamespace`
/// is 'x', then that middleware will be available as 'x.y' in the main application.
/// These namespaces can be nested.
use(Pattern path, Routable routable,
2016-06-21 04:19:43 +00:00
{bool hooked: true, String middlewareNamespace: null}) {
Routable _routable = routable;
// If we need to hook this service, do it here. It has to be first, or
// else all routes will point to the old service.
if (_routable is Service) {
Hooked hookedDeclaration = _getAnnotation(_routable, Hooked);
Service service = (hookedDeclaration != null || hooked)
? new HookedService(_routable)
: _routable;
services[path.toString().trim().replaceAll(
new RegExp(r'(^\/+)|(\/+$)'), '')] = service;
_routable = service;
}
requestMiddleware.addAll(_routable.requestMiddleware);
for (Route route in _routable.routes) {
2016-04-18 03:27:23 +00:00
Route provisional = new Route('', path);
if (route.path == '/') {
route.path = '';
route.matcher = new RegExp(r'^\/?$');
}
2016-04-18 03:27:23 +00:00
route.matcher = new RegExp(route.matcher.pattern.replaceAll(
new RegExp('^\\^'),
provisional.matcher.pattern.replaceAll(new RegExp(r'\$$'), '')));
route.path = "$path${route.path}";
routes.add(route);
}
// Let's copy middleware, heeding the optional middleware namespace.
String middlewarePrefix = "";
if (middlewareNamespace != null)
middlewarePrefix = "$middlewareNamespace.";
2016-06-21 04:19:43 +00:00
for (String middlewareName in _routable.requestMiddleware.keys) {
requestMiddleware["$middlewarePrefix$middlewareName"] =
2016-06-21 04:19:43 +00:00
_routable.requestMiddleware[middlewareName];
}
// Copy services, too. :)
2016-06-21 04:19:43 +00:00
for (Pattern servicePath in _routable.services.keys) {
String newServicePath = path.toString().trim().replaceAll(
new RegExp(r'(^\/+)|(\/+$)'), '') + '/$servicePath';
2016-06-21 04:19:43 +00:00
services[newServicePath] = _routable.services[servicePath];
2016-04-18 03:27:23 +00:00
}
}
/// Adds a route that responds to the given path
/// for requests with the given method (case-insensitive).
/// Provide '*' as the method to respond to all methods.
Route addRoute(String method, Pattern path, Object handler,
{List middleware}) {
List handlers = [];
// Merge @Middleware declaration, if any
Middleware middlewareDeclaration = _getAnnotation(
handler, Middleware);
if (middlewareDeclaration != null) {
handlers.addAll(middlewareDeclaration.handlers);
}
handlers
..addAll(middleware ?? [])
..add(handler);
var route = new Route(method.toUpperCase().trim(), path, handlers);
routes.add(route);
return route;
}
/// Adds a route that responds to any request matching the given path.
Route all(Pattern path, Object handler, {List middleware}) {
return addRoute('*', path, handler, middleware: middleware);
}
/// Adds a route that responds to a GET request.
Route get(Pattern path, Object handler, {List middleware}) {
return addRoute('GET', path, handler, middleware: middleware);
}
/// Adds a route that responds to a POST request.
Route post(Pattern path, Object handler, {List middleware}) {
return addRoute('POST', path, handler, middleware: middleware);
}
/// Adds a route that responds to a PATCH request.
Route patch(Pattern path, Object handler, {List middleware}) {
return addRoute('PATCH', path, handler, middleware: middleware);
}
/// Adds a route that responds to a DELETE request.
Route delete(Pattern path, Object handler, {List middleware}) {
return addRoute('DELETE', path, handler, middleware: middleware);
}
2016-04-18 03:27:23 +00:00
Routable() {
}
2016-02-28 13:11:17 +00:00
}