In Twirl reverse routing is as easy as the inline Scala operator -
@controllers.routes.HomeController.loginSubmit. The same for the Java or Scala code. Just the
routes package need to be added before the controller class name.
I implement the reves routing helper in my handlebars templates with the help of reflection.
It's no need even to add the rotes package
...
<form action="{{route "controllers.HomeController.loginSubmit"}}" method="POST>
...
{{route "controllers.HomeController.myAction()"}}
{{route "controllers.HomeController.myActionName(\"name\")"}}
{{route "controllers.HomeController.myActionAge(33)"}}
The core method of realization is the reflection of the correspond routes class:
- Get the class loader.
- Load the auto generated class routes.
- Get the reverse router object of the controller (it's in a static field of the routes class).
- Get the action of the reverse controller (do not forget about method parameters).
- Get the URL of the action.
Realization
private static String reverseUrl(
final String controllerPackage,
final String controllerClass,
final String methodName,
final RouteMethodArguments methodArguments) throws Exception {
// Get the play class loader.
final ClassLoader classLoader = Play.classloader(Play.current());
// Load the auto generated class "routes".
final Class routerClass = classLoader.loadClass(controllerPackage + ".routes");
// Get the reverse router object of the controller.
final Field declaredField = routerClass.getDeclaredField(controllerClass);
// It's static field.
final Object object = declaredField.get(null);
final Class type = declaredField.getType();
// Get the action of the reverse controller.
final Method routerMethod = type.getMethod(methodName, methodArguments.types);
final Call invoke = (Call) routerMethod.invoke(object, methodArguments.values);
// Get the URL of the action.
final String actionUrl = invoke.url();
return actionUrl;
}
There are also some trivial code for the parsing helper parameter and for the caching. For now, I support only the String and Integer parameters for the actions. The cashe system is
the guava cache.
One more thing - the RouteMethodArguments class that represents the arguments of the action
private static class RouteMethodArguments {
final Class<?>[] types;
final Object[] values;
RouteMethodArguments(Class<?>[] types, Object[] values) {
this.types = types;
this.values = values;
}
}