Controllers
The metadata on controller classes is processed via reflection only once, at startup. Do not believe that your controllers will be crippled by reflection during request handling, because that possibility is eliminated by pre-injecting dependencies.
import 'package:angel3_framework/angel3_framework.dart';
import 'package:angel3_container/mirrors.dart';
@Expose("/todos")
class TodoController extends Controller {
@Expose("/:id")
getTodo(id) async {
return await someAsyncAction();
}
// You can return a response handler, and have it run as well. :)
@Expose("/login")
login() => auth.authenticate('google');
}
main() async {
Angel app = Angel(reflector: MirrorsReflector());
await app.configure(TodoController().configureServer);
}
Rather than extending from
Routable
, controllers act as plugins when called. This pseudo-plugin will wire all your routes for you.The glue that holds it all together is the
Expose
annotation:class Expose {
final String method;
final Pattern path;
final List middleware;
final String as;
final List<String> allowNull;
const Expose(Pattern this.path,
{String this.method: "GET",
List this.middleware: const [],
String this.as: null,
List<String> this.allowNull: const[]});
}
Most fields are self-explanatory, save for
as
and allowNull
. See, request parameters are mapped to function parameters on each handler. If a parameter is null
, an error will be thrown. To prevent this, you can pass its name to allowNull
.@Expose("/foo/:id?", allowNull: const["id"])
The other is
as
. This allows you to specify a custom name for a controller class or action. ResponseContext
contains a method, redirectToAction
that can redirect to a controller action.@Expose("/foo")
class FooController extends Controller {
@Expose("/some/strange/url/:id", as: "bar")
someActionWithALongNameThatWeWouldLikeToShorten(int id) async {
}
}
main() async {
Angel app = Angel();
app.get("/some/path", (req, res) async => res.redirectToAction("FooController@bar", {"id": 1337}));
}
If you do not specify an
as
, then controllers and actions will be available by their names in code. Reflection is cool, huh?Controllers can also interact with requests and responses. All you have to do is declare a
RequestContext
or ResponseContext
as a parameter, and it will be passed to the function.@Expose("/hello")
class HelloController extends Controller {
@Expose("/")
Future getIndex(ResponseContext res) async {
await res.render("hello");
}
}
Future<bool> deserializeUser(RequestContext req, res) async {
var id = req.params['id'] as String;
req.params['user'] = await asyncFetchUser(id);
return true;
}
@Expose("/user", middleware: const [deserializeUser])
class UserController extends Controller {
@Expose("/:id/name")
Future<String> getUserName(User user) async {
return user.username;
}
}
main() async {
Angel app = Angel();
await app.configure(UserController().configureServer);
}
- 1.
Last modified 1yr ago