Conduit HTTP Snippets
Hello, World
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router.route("/hello_world").linkFunction((request) async {
return Response.ok("Hello, world!")
..contentType = ContentType.text;
});
return router;
}
}
Route Variables
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router.route("/variable/[:variable]").linkFunction((request) async {
return Response.ok({
"method": request.raw.method,
"path": request.path.variables["variable"] ?? "not specified"
});
});
return router;
}
}
Grouping Routes and Binding Path Variables
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router
.route("/users/[:id]")
.link(() => MyController());
return router;
}
}
class MyController extends ResourceController {
final List<String> things = ['thing1', 'thing2'];
@Operation.get()
Future<Response> getThings() async {
return Response.ok(things);
}
@Operation.get('id')
Future<Response> getThing(@Bind.path('id') int id) async {
if (id < 0 || id >= things.length) {
return Response.notFound();
}
return Response.ok(things[id]);
}
}
Custom Middleware
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router
.route("/rate_limit")
.link(() => RateLimiter())
.linkFunction((req) async => Response.ok({
"requests_remaining": req.attachments["remaining"]
}));
return router;
}
}
class RateLimiter extends RequestController {
@override
Future<RequestOrResponse> handle(Request request) async {
final apiKey = request.raw.headers.value("x-apikey");
final requestsRemaining = await remainingRequestsForAPIKey(apiKey);
if (requestsRemaining <= 0) {
return Response(429, null, null);
}
request.addResponseModifier((r) {
r.headers["x-remaining-requests"] = requestsRemaining;
});
return request;
}
}
Application-Wide CORS Allowed Origins
class AppChannel extends ApplicationChannel {
@override
Future prepare() async {
// All controllers will use this policy by default
CORSPolicy.defaultPolicy.allowedOrigins = ["https://mywebsite.com"];
}
@override
Controller get entryPoint {
final router = Router();
router.route("/things").linkFunction((request) async {
return Response.ok(["Widget", "Doodad", "Transformer"]);
});
return router;
}
}
Serve Files and Set Cache-Control Headers
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router.route("/files/*").link(() =>
FileController("web")
..addCachePolicy(new CachePolicy(expirationFromNow: new Duration(days: 365)),
(path) => path.endsWith(".js") || path.endsWith(".css"))
);
return router;
}
}
Streaming Responses (Server Side Events with text/event-stream)
class AppChannel extends ApplicationChannel {
final StreamController<String> controller = new StreamController<String>();
@override
Future prepare() async {
var count = 0;
Timer.periodic(new Duration(seconds: 1), (_) {
count ++;
controller.add("This server has been up for $count seconds\n");
});
}
@override
Controller get entryPoint {
final router = new Router();
router.route("/stream").linkFunction((req) async {
return Response.ok(controller.stream)
..bufferOutput = false
..contentType = new ContentType(
"text", "event-stream", charset: "utf-8");
});
return router;
}
}
A websocket server
class AppChannel extends ApplicationChannel {
List<WebSocket> websockets = [];
@override
Future prepare() async {
// When another isolate gets a websocket message, echo it to
// websockets connected on this isolate.
messageHub.listen(sendBytesToConnectedClients);
}
@override
Controller get entryPoint {
final router = Router();
// Allow websocket clients to connect to ws://host/connect
router.route("/connect").linkFunction((request) async {
var websocket = await WebSocketTransformer.upgrade(request.raw);
websocket.listen(echo, onDone: () {
websockets.remove(websocket);
}, cancelOnError: true);
websockets.add(websocket);
// Take request out of channel
return null;
});
return router;
}
void sendBytesToConnectedClients(List<int> bytes) {
websockets.forEach((ws) {
ws.add(bytes);
});
}
void echo(List<int> bytes) {
sendBytesToConnectedClients(bytes);
// Send to other isolates
messageHub.add(bytes);
}
}
Setting Content-Type and Encoding a Response Body
class AppChannel extends ApplicationChannel {
final ContentType CSV = ContentType("text", "csv", charset: "utf-8");
@override
Future prepare() async {
// CsvCodec extends dart:convert.Codec
CodecRegistry.defaultInstance.add(CSV, new CsvCodec());
}
@override
Controller get entryPoint {
final router = Router();
router.route("/csv").linkFunction((req) async {
// These values will get converted by CsvCodec into a comma-separated string
return Response.ok([[1, 2, 3], ["a", "b", "c"]])
..contentType = CSV;
});
return router;
}
}
Proxy a File From Another Server
class AppChannel extends ApplicationChannel {
@override
Controller get entryPoint {
final router = Router();
router.route("/proxy/*").linkFunction((req) async {
var fileURL = "https://otherserver/${req.path.remainingPath}";
var fileRequest = await client.getUrl(url);
var fileResponse = await req.close();
if (fileResponse.statusCode != 200) {
return new Response.notFound();
}
// A dart:io.HttpResponse is a Stream<List<int>> of its body bytes.
return new Response.ok(fileResponse)
..contentType = fileResponse.headers.contentType
// let the data just pass through because it has already been encoded
// according to content-type; applying encoding again would cause
// an issue
..encodeBody = false;
});
return router;
}
}