Routes

To bind a function with an URL, just use the @Route annotation

@app.Route("/")
helloWorld() => "Hello, World!";

Redstone.dart will serialize the returned value for you. So, if your function returns a List or a Map, the client receives a JSON object:

@app.Route("/user/find/:id")
getUser(String id) => {"name": "User", "login": "user"};

If your function depends on async operations, you can also return a Future

@app.Route("/service")
service() => doSomeAsyncOperation().then((_) => {"success": true});

You can easily bind path segments and query parameters

@app.Route("/user/find/:type")
findUsers(String type, @app.QueryParam() String name) {
  // ...
}

You can also bind the request body

@app.Route("/user/add", methods: const [app.POST])
addUser(@app.Body(app.JSON) Map user) {
  // ...
}

It’s also possible to access the current request object

@app.Route("/service", methods: const [app.GET, app.POST])
service() {
  if (app.request.method == app.GET) {
    // ...
  } else if (app.request.method == app.POST) {
    if (app.request.bodyType == app.JSON) {
      var json = app.request.body;
      // ...
    } else {
      // ...
    }
  }
};

Interceptors

Interceptors are useful when you need to apply a common behavior to a group of targets (functions or static content). For example, you can create an interceptor to apply a security constraint or to manage a resource

@app.Interceptor(r'/admin/.*')
adminFilter() {
  if (app.request.session["username"] != null) {
    return app.chain.next();
  } else {
    return app.chain.abort(HttpStatus.UNAUTHORIZED);
    //or app.chain.redirect("/login.html");
  }
}
@app.Interceptor(r'/services/.+')
dbConnInterceptor() async {
  var conn = new DbConn();
  
  app.request.attributes["dbConn"] = conn;
  var response = await app.chain.next();
  
  await conn.close()
  
  return response;
}

@app.Route('/services/find')
find(@app.Attr() dbConn) {
  // ...
}

Error Handlers

Use the @ErrorHandler annotation to register error handlers.

@app.ErrorHandler(404)
handleNotFoundError() => app.redirect("/error/not_found.html");
@app.ErrorHandler(500)
handleServerError() {
  print(app.chain.error);
  return new shelf.Response.internalServerError(body: "Server Error.");
}

Groups

You can use classes to group routes, interceptors and error handlers

@Group("/user")
class UserService {
  
  @app.Route("/find")
  findUser(@app.QueryParam("n") String name,
           @app.QueryParam("c") String city) {
    // ...
  }

  @app.Route("/add", methods: const [app.POST])
  addUser(@app.Body(app.JSON) Map json) {
    // ...
  }
}

Dependency Injection

Register one or more modules before calling app.start()

import 'package:redstone/redstone.dart' as app;
import 'package:di/di.dart';

main() {
  app.addModule(new Module()
      ..bind(ClassA)
      ..bind(ClassB));

  app.setupConsoleLog();
  app.start();
}

Source code

Routes, interceptors, error handlers and groups can require dependencies

@app.Route('/service')
service(@app.Inject() ClassA objA) {
 // ...
}
@app.Interceptor(r'/services/.+')
interceptor(ClassA objA, ClassB objB) {
  // ...
}
@app.ErrorHandler(404)
notFound(ClassB objB) {
  // ...
}
@app.Group('/group')
class Group {
  ClassA objA;
  
  Group(ClassA this.objA);
  
  @app.Route('/service')
  service() {
    // ...
  }
}

Unit tests

You can easily create mock requests to test your server

library services;

import 'package:redstone/redstone.dart' as app;

@app.Route("/user/:username")
helloUser(String username) => "hello, $username";

Source code

import 'package:test/test.dart';
import 'package:redstone/redstone.dart' as app;
import 'package:your_package_name/services.dart';

main() {
  // Load handlers in 'services' library
  setUp(() => app.redstoneSetUp([#services]));

  // Remove all loaded handlers
  tearDown(() => app.redstoneTearDown());

  test("hello service", () {
    // Create a mock request
    var req = new app.MockRequest("/user/luiz");
    // Dispatch the request
    return app.dispatch(req).then((resp) {
      // Verify the response
      expect(resp.statusCode, equals(200));
      expect(resp.mockContent, equals("hello, luiz"));
    });
  });
}

Source code