Framework for specifing and mapping rest routes. It is based on the standard HttpRequest object
Features:
- Inline definition of routes/child routes
- Annotations
- Encoding/Decoding from Json to Object and Object to Json
- Exception Handling
The router is the central class for initialising the routing and defining rest routes.
Initialise the router by creating a new instance of the router class.
Router router = new Router();
Routers also support some kind of base paths
Router router = new Router("/stock");
Routers can be nested (again base paths are optional).
Router router = new Router("/stock");
Router childrouter = router.child("/bond");
It is also possible to initialise the router by annotating the classes as rest resources.
@RestResource("/bond")
class RestResource {
...
}
Again base path is optional
@RestResource()
class RestResource {
...
}
The router is intialised by using the fromAnnotation constructor
Router router = Router.fromAnnotation();
Only the routes in the same isolate will be created/found.
Routes are resolved by passing a HttpRequest to the route method of a registered router
HttpServer.bind(host, port).then((HttpServer server) {
server.listen((HttpRequest request) {
router.route(request);
});
});
The router provides methods for registering rest routes. Routes can be defined inline or by using annotations.
It is recommened to either use annotations or defining routes inline. Mixing annotations and inline registering of routes is not fully supported/tested, has known limitations and could lead to issues
Router router = new Router();
router.get(toCall, "/bond/{id}");
router.post(toCall, "/bond");
router.put(toCall, "/bond/{id}");
router.delete(toCall, "/bond/{id}");
@RestResource("/bond")
class RestResource {
@RestMethod(HttpMethod.get, "/{id}")
void getMethod() {
}
@RestMethod(HttpMethod.post)
void postMethod() {
}
@RestMethod(HttpMethod.put, "/{id}")
void putMethod() {
}
@RestMethod(HttpMethod.delete, "/{id}")
void deleteMethod() {
}
}
Routes to childs are also possible
@RestResource("/stock")
class RestResource {
@RestMethod(HttpMethod.get, "/{id}")
void getMethod() {
}
@RestMethod(HttpMethod.get, "/{stockId}/bond/{bondId}")
void getMethodChild() {
}
}
In case more than one route are registered for a specific url the http request is mapped to the first match.
The framework supports url parameters and path parameters
Url parameters are included in the url by surrounding them with curly brackets
Examples:
Routes:
http://localhost:99/stocks/{id}
http://localhost:99/stocks/{stockId}/bonds/{bondId}
Mappings:
http://localhost:99/stocks/1
http://localhost:99/stocks/2/bonds/4
Path parameters are specified at the end of the url starting with a "?"
Examples:
Routes:
http://localhost:99/stocks{?type}{?filter}
http://localhost:99/stocks/{stockId}/bonds{?from}{?to}
Mappings:
http://localhost:99/stocks?type=mytype&filter=new
http://localhost:99/stocks?type=mytype
http://localhost:99/stocks/2/bonds?from=1428012000000&to=1428142974369
As described the methods for the different routes are specified inline or by using annotations. The framework supports mapping the url- and path parameters to the respective method parameters. If required the HttpRequest can also be passed to the method
The request body as well as the repsonse are decoded/encoded from/to Json.
The frameworks maps the url-/path parameters to a specific method parameter by using the same name or using the path param annotation
@RestResource("/stock")
class RestResource {
@RestMethod(HttpMethod.get, "/{?name}")
void getMethod(String name) {
}
@RestMethod(HttpMethod.get, "/{stockId}/bond/{bondId}{?name}")
void testResourceNoPath(int stockId, int bondId, String name) {
}
}
By using the path param annotation the name of the method parameter can be different from the url-/path param
@RestResource("/stock")
class RestResource {
@RestMethod(HttpMethod.get, "/{stockId}/{?name}")
void getMethod(@PathParam() stockId, @PathParam("name") String stockName) {
}
@RestMethod(HttpMethod.get, "/{stockId}/bond/{bondId}{?name}")
void testResourceNoPath(@PathParam("stockId") int stock, int bondId, String name) {
}
}
As of yet path params are only supported when resources and routes are specified using annotations
In case a method requires the original HttpRequest object it just needs to be added as parameter to the method
void method(HttpRequest request, String id) {
}
By annotating a method parameter with the "RequestBody" annotation the framework tries to create the object from the provided json String. Therefore the parameter needs to be typed and a "fromJson" constructor is required
class MyObject {
MyObject.fromJson(Map<String, dynamic> json) {
...
}
}
void method(HttpRequest request, @RequestBody() MyObject body) {}
In case the annotated parameter is dynamic, the json string is passed through.
As for the request body parameter the return value of a method is parsed to a json string in case it is typed and a toJson method is provided
class MyObject {
MyObject.fromJson(Map<String, dynamic> json) {
...
}
Map<String, dynamic> toJson() {
Map<String, dynamic> jsonMap = new Map<String, dynamic>();
...
return jsonMap;
}
}
MyObject method(HttpRequest request) {
MyObject object = new MyObject();
...
return object;
}
Returning a simple type like string or int is also possible
String method(HttpRequest request) {
String returnString;
...
return returnString;
}
The framework handles some basic exceptions. For a custom exception hanlding exception handlers can be registered
- 404 - Not found
- Returned in case the route is not found
- 500 - Internal Error
- Returned in case an unexpected error occurs
All standard errors can be overwritten by registering a custom error handler
The router class provides a method for registering an error handler.
The error handler is required to extend the abstract ErrorHandler class
abstract class ErrorHandler {
void handleError(HttpRequest request, e);
}
The handler can then be registered for a router instance
class MyErrorHandler extends ErrorHandler {
void handleError(request, e) {
request.response.statusCode = HttpStatus.BAD_REQUEST;
request.response.close();
}
}
Router router = new Router.fromAnnotation();
router.registerErrorHanlder(new MyErrorHandler());
In case more than one handler is registered the handlers are called in FIFO order.
- PathParam annotation is not supported when routes are defined inline.
- Docu
- Further testing
- Optimications
- Support defining routes inline and with annotations (mixed mode)