samedi 20 mai 2017

Créer un router avec la librairie shelf

Avec Dart et la librairie shelf il est très simple de créer un serveur HTTP pour servir des pages statiques ou créer une API REST.

Mais il n'est rien prévu d'origine pour avoir un router qui permet de faire les deux en même temps, voir même plusieurs API REST.

J'ai bien proposé de l'intégrer directement, mais cela n'a pas été accepté pour des raisons que je comprend bien. Je vais donc partager ici ma solution.

Créer un fichier nommé shelf_router.dart et copier/coller :
import 'package:shelf/shelf.dart';
import 'dart:core';

/// Creates a Shelf [Handler] that allow to register some Shelf [Handler] like
/// [createStaticHandler] or [createRpcHandler] on same listener.
/// Just give map [routing] to link [key] that represent path to [value] that
/// contain handler.
/// The path [key] is only static path.
Handler createSubHandler(Map routing) {
  return (Request request) {
    // Selected path
    String selectedPath = '';

    routing.forEach((key, value) {
      String currentPath = '/' + request.url.path;

      if (currentPath.startsWith(key)
        && currentPath.length > selectedPath.length) {
        selectedPath = key;
      }
    });

    Handler h = routing[selectedPath];

    if (h == null) {
      return new Response.notFound('Not Found');
    } else {
      Uri requestUri = request.requestedUri;

      Uri newRequestedUri = new Uri(
        scheme: requestUri.scheme,
        userInfo: requestUri.userInfo,
        // Getter return empty string if host is null
        host: requestUri.host.length == 0 ? null : requestUri.host,
        port: requestUri.port,
        path: requestUri.path.substring(selectedPath.length - 1),
        query: requestUri.query,
        queryParameters: requestUri.queryParameters,
        fragment: requestUri.fragment);

      Uri url = request.url;

      Uri newUrl = new Uri(
        scheme: url.scheme,
        userInfo: url.userInfo,
        // Getter return empty string if host is null
        host: url.host.length == 0 ? null : url.host,
        port: url.port,
        path: url.path.substring(selectedPath.length - 1),
        query: url.query,
        queryParameters: url.queryParameters,
        fragment: url.fragment);

      Request newRequest = new Request(request.method, newRequestedUri,
        protocolVersion: request.protocolVersion,
        headers: request.headers,
        handlerPath: request.handlerPath,
        url: newUrl,
        body: request.readAsString().asStream(),
        encoding: request.encoding,
        context: request.context,
        onHijack: request.hijack);

      return h(newRequest);
    }
  };
}

Ensuite pour l'utiliser, rien de plus simple :
// Create a Shelf handler for your RPC API.
var handlerApi = shelf_rpc.createRpcHandler(apiServer);
// Create a Shelf handler for static file
var handlerStatic = shelf_static.createStaticHandler('example/files', defaultDocument: 'index.html');

var handler = shelf_router.createSubHandler({
  '/': handlerStatic,
  '/api/': handlerApi,
  '/api/toto/': otherHandler
});

Il vous suffit de créer une map qui associe le path et le handler.
Le système vérifie le path le plus long pour que comme dans l'exemple, il n'y ait pas collision même si le début du path est identique.

A noter que le router ne supporte pas les variables dans les path. C'est logique car ici, ça n'aurait pas de sens.

Aucun commentaire:

Publier un commentaire