Organisation des Routings in der Clojure-Webanwendung

    Es gibt Bibliotheken in verschiedenen Sprachen, die gemeinsame Funktionen haben. Dies sind Compojure, Sinatra, Traube, Express, Koa und dergleichen.


    Sie haben einen ähnlichen Ansatz für das Routing. Sie legen keine Einschränkungen fest und bieten keine Struktur zum Organisieren von URLs. Entwickler unter solchen Bedingungen kümmern sich in der Regel nicht um die Struktur und erhalten anschließend schlecht unterstützten Code.


    Ein weiteres gemeinsames Merkmal ist die Einseitigkeit. Das heißt, Eine bestimmte Anforderung entspricht einem bestimmten Handler. Entwickler sind gezwungen, URLs mit Zeichenfolgen in Vorlagen zu registrieren. Es gibt keine Möglichkeit, in Form eines Sprachkonstrukts anzugeben, welche URL generiert werden soll. Dies führt dazu, dass tote Links in den Ansichten verbleiben und es keine Möglichkeit gibt, sie zu finden, außer alle Seiten zu durchbohren.


    Ich zeige Ihnen, wie Sie die Codeunterstützung im Clojure-Ökosystem verbessern und wie:


    1. URLs organisieren
    2. Struktur-Handler-Code
    3. Verwenden Sie Sprachkonstrukte, um eine URL zu generieren

    Es ist wichtig zu verstehen, dass die oben aufgeführten Bibliotheken über Tools zum Organisieren von Handlern in Modulen verfügen. Sie können die URL einer Zeichenfolge manuell in eine Funktion einschließen und nur diese Funktionen verwenden. Aber in der Regel denken sie zu spät darüber nach.


    Ich bin ein Ruby-Entwickler und in anderen Ökosystemen (clojure, js, erlang, go) fehlt mir eine Routing-Organisation wie Rails. Ich vermisse REST und das Konzept der "Ressource". Ich vermisse die Controller. Ich habe nicht genug Helfer für die URL-Generierung admin_page_path(@page).


    Wenn Sie mit Rails nicht vertraut sind, finden Sie hier einen Link zum Rails Routing von der Outside In Routing-Beschreibung . Wenn Sie vergessen haben, was HTTP oder REST ist, empfehle ich Ihnen, den kurzen und humorvollen Artikel "15 triviale Fakten zur korrekten Arbeit mit dem HTTP-Protokoll" oder den ausführlicheren Artikel "Warum benötigen Sie dieses REST sowie einige Details zur Implementierung von RESTful-Anwendungen" zu lesen .


    Verwenden Sie also REST, um die URL zu organisieren.


    Um die verbleibenden zwei Punkte zu behandeln, werde ich Beispiele für die Verwendung meiner Darkleaf / Router- Bibliothek zeigen . Weil Es ist ein Clojure-Ökosystem, dann natürlich ein ringkompatibles Routing.


    (ns hello-world.core
      (:require [darkleaf.router :refer :all]))
    (def pages-controller
      {:middleware (fn [handler] (fn [req] req))
       :member-middleware some-other-middleware
       :index (fn [req] some-ring-response)
       :show (fn [req] some-ring-response)})
    (def routes
      (build-routes
       (resources :pages 'page-id pages-controller)))
    (def handler (build-handler routes))
    (def request-for (build-request-for routes))
    (handler {:uri "/pages", :request-method :get}) ;; call index action from pages-controller
    (request-for :index [:pages]) ;; returns {:uri "/pages", :request-method :get}
    (handler {:uri "/pages/1", :request-method :get}) ;; call show action from pages-controller
    (request-for :show [:pages] {:page-id "1"}) ;; returns {:uri "/pages/1", :request-method :get}

    Hier wird wie bei Schienen eine Steuerung deklariert. In diesem Fall mit zwei Aktionen: index und show.
    Ein Controller ist nur eine Karte, und Sie können beispielsweise mit Ihrer Funktion leicht unterschiedliche Controller erstellen.
    Routen ist ein flacher Vektor von Routen, die durch Funktionen wie Ressourcen generiert werden.
    Handler ist ein ringkompatibler Anforderungshandler, und request-for wird verwendet, um eine Anforderung mit dem Namen der Route und ihrem Umfang zu empfangen. Sie werden mit Makros generiert, weil Verwenden Sie inside core.match.


    Der Controller darf nur die folgenden Schlüssel enthalten:


    • : Middleware - Umschließt alle Controller-Aktionen, einschließlich Handler für verschachtelte Routen
    • : member-middleware - Umschließt nur Mitgliederaktionen und verschachtelte Routenhandler, die als Mitglied markiert sind
    • Sammlungsaktionen :: index ,: new ,: create
    • Mitgliederaktionen :: show ,: edit ,: update ,: destroy

    Hier ist ein vollständiges Beispiel für eine Ressource:


    (resources :pages 'page-id {:index identity
                                :new identity
                                :create identity
                                :show identity
                                :edit identity
                                :update identity
                                :destroy identity}
               :collection
               [(action :archived identity)]
               :member
               [(resources :comments 'comment-id {:index identity})])

    Das erste Argument gibt den Namen der Ressourcen an und legt das Segment in der URL fest. Der zweite Parameter legt den Namen der Ressourcen-ID fest.


    Wie oben erwähnt, können Ressourcen verschachtelte Routen zweier Typen enthalten: Sammlung und Mitglied.


    ;; pages collection routes
    (request-for :archived [:pages] {}) ;; #=> {:uri "/pages/archived", :request-method :get}
    ;; pages member routes
    (request-for :index [:pages :comments] {:page-id "some-id"}) ;; #=> {:uri "/pages/some-id/comments", :request-method :get}

    Zusätzlich zum Funktionsgenerator von Routen haben Ressourcen auch: root, action, wildcard, not-found, scope, guard, resource. Ich werde nicht auf sie eingehen, Sie werden detaillierte Beispiele für ihre Verwendung in Tests finden .


    Es gibt keine Möglichkeit, dem Controller neue Aktionen hinzuzufügen. Dies ist eine absichtliche Einschränkung, die auf REST und verschachtelte Ressourcen drängt. Wenn Sie noch zusätzliche Aktionen benötigen und die angehängten Ressourcen nicht verwenden möchten, können Sie die angehängte Route verwenden, wie oben für die Route gezeigt: Archiviert. Es wird jedoch eine separate Funktion außerhalb des Controllers sein.


    Wie Sie bereits bemerkt haben, request-forwird im Gegensatz zu Rail-Helfern, die nur URLs zurückgeben, die gesamte Anforderungsstruktur zurückgegeben. Dies ist nützlich, wenn Header oder andere Anforderungsparameter wie Host zum Definieren des Handlers verwendet werden. In zukünftigen Versionen ist die Unterstützung von Clojurescript geplant, und Sie können dieselbe Anforderung verwenden, um Backend-Anforderungen zu erstellen.


    Weil request-forWenn die gesamte Anforderung zurückgegeben wird, wird überprüft, ob die empfangene Anforderung in den gewünschten Handler fällt. Dies ist nützlich, wenn 2 ähnliche URLs unterschiedlich behandelt werden oder eine Einschränkung vorliegt.


    (guard :locale #{"ru" "en"}
           (action :localized-page identity))
    (not-found itentity)       

    (request-for :localized-page [:locale] {:locale "it"})wird einen Fehler geben, weil Der Handler /it/localized-pagewird die Route sein not-found, nicht :localized-page [:locale].


    Die Bibliothek ist in zwei Namespace unterteilt:


    darkleaf.router und darkleaf.router.low-level . Wenn Sie spezielle Routing-Anforderungen haben oder das alte URL-Schema unterstützen müssen, können Sie Ihre Funktionen wie in darkleaf.router auf die Ebene darkleaf.router.low schreiben.


    Vollständige Anwendungsbeispiele finden Sie in den Tests darkleaf.router-test und darkleaf.router.low-level-test .


    Die Bibliothek verwendet innere core.matchund ziemlich unterhaltsame Makros. Dies ist jedoch das Thema eines separaten Artikels. Schreiben Sie in die Kommentare, wenn Sie interessiert sind, wie dies im Inneren funktioniert.


    UPDATE 10-02-2017 veröffentlichte Version 0.2, ohne Makros und Bibliotheken von Drittanbietern, unterstützt Clojurescript https://github.com/darkleaf/router


    Jetzt auch beliebt: