Frage

Was ist Rack-Middleware in Ruby? Ich konnte keine gute Erklärung finden, was sie unter „Middleware“.

War es hilfreich?

Lösung

Rack als Design

Rack-Middleware mehr als „eine Möglichkeit, eine Anfrage und Antwort zu filtern“ - es ist eine Implementierung des Pipeline-Entwurfsmuster für Web-Server mit Hilfe von Rack-.

Es ist sehr sauber trennt die verschiedene Stadien der Verarbeitung eine Anforderung -. Trennung von Bedenken ein wichtiges Ziel aller gut entwickelte Software-Produkte zu sein

Zum Beispiel mit Rack I getrennten Stufen der Pipeline haben kann tun:

  • Authentication : Wenn die Anfrage kommt, werden die Benutzer Anmeldedaten korrekt? Wie bestätige ich diesen OAuth, HTTP Basic Authentication, Name / Passwort?

  • Authorization : "? Ist der Benutzer diese bestimmte Aufgabe zu erfüllen autorisiert", das heißt die rollenbasierte Sicherheit

  • Caching : habe ich diese Anfrage bereits bearbeitet, kann ich ein im Cache gespeicherte Ergebnis zurück

  • Dekoration : Wie kann ich die Anfrage verbessern Downstream Processing besser zu machen

  • Performance & Usage Überwachung : Welche Statistiken kann ich von der Anfrage und Antwort bekommen

  • Ausführung :. Tatsächlich die Anforderung verarbeiten und eine Antwort liefern

Die Möglichkeit, die verschiedenen Phasen zu trennen (und sind sie optional) ist eine große Hilfe bei der gut strukturierten Anwendungen zu entwickeln.

Community

Es gibt auch ein großes Öko-System zu entwickeln um Middleware-Rack - Sie sollten in der Lage sein, vorgefertigte Rack-Komponenten zu finden alle Schritte oben und mehr zu tun. Siehe die Rack GitHub Wiki für eine Liste von Middleware .

Was ist Middleware?

Middleware ist eine schreckliche Begriff, der auf jede Softwarekomponente verweist / Bibliothek, die mit unterstützt, aber nicht direkt in der Ausführung einiger Aufgabe beteiligt. Sehr häufig Beispiele sind Logging, Authentifizierung und die andere gemeinsame, horizontale Verarbeitungskomponenten . Diese neigen dazu, die Dinge zu sein, dass jeder über mehrere Anwendungen braucht aber nicht zu viele Leute daran interessiert sind (oder sein sollte) an sich selbst zu bauen.

Weitere Informationen

Andere Tipps

Zu allererst Rack ist genau zwei Dinge:

  • Ein Webserver-Schnittstelle Konvention
  • A gem

Rack - Die Webserver-Schnittstelle

Die Grundlagen der Zahnstange ist eine einfache Konvention. Jeder Rack-konformer Web-Server wird ein Gespräch immer Methode für ein Objekt aufrufen Sie ihnen geben und das Ergebnis dieser Methode dienen. Rack gibt an, wie genau diese Call-Methode zu suchen hat, wie und was es hat zurückzukehren. Das ist, Rack.

Geben wir es einfach versuchen. Ich werde verwenden WEBrick als Rack-kompatible Webserver, aber jeder von ihnen tun wird. Lassen Sie uns erstellen Sie eine einfache Web-Anwendung, dass die Renditen ein JSON-String. Dazu werden wir eine Datei namens config.ru erstellen. Die config.ru wird automatisch vom Rack gem Befehl rackup genannt werden, die den Inhalt des config.ru in einem Rack-kompatibelen Webserver einfach ausgeführt werden. Also lassen Sie uns die folgende auf die config.ru-Datei hinzu:

class JSONServer
  def call(env)
    [200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
  end
end

map '/hello.json' do
  run JSONServer.new
end

als Konvention spezifiziert unser Server eine Methode namens Aufruf hat, die eine Umgebung Hash annimmt und gibt einen Array mit der Form [Status, Überschriften, body] für den Web-Server zu dienen. Lassen Sie uns versuchen Sie es aus, indem Sie einfach rackup aufrufen. Ein Standard-Rack-konformen Server, vielleicht WEBrick oder Mischlings starten und sofort auf Anfragen warten zu dienen.

$ rackup
[2012-02-19 22:39:26] INFO  WEBrick 1.3.1
[2012-02-19 22:39:26] INFO  ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO  WEBrick::HTTPServer#start: pid=16121 port=9292

Lassen Sie uns Test unserer neuen JSON-Server entweder durch Kräuseln oder besuchen Sie die URL http://localhost:9292/hello.json und voila:

$ curl http://localhost:9292/hello.json
{ message: "Hello!" }

Es funktioniert. Toll! Das ist die Grundlage für jeden Web-Framework, sei es Rails oder Sinatra. An einem gewissen Punkt sie einen Anruf Methode, Arbeit durch all Framework-Code, implementieren und schließlich eine Antwort im typischen return [Status, Kopf-, Körper] Form.

In Ruby on Rails zum Beispiel der Rack-Anfragen trifft die ActionDispatch::Routing.Mapper Klasse, die wie folgt aussieht:

module ActionDispatch
  module Routing
    class Mapper
      ...
      def initialize(app, constraints, request)
        @app, @constraints, @request = app, constraints, request
      end

      def matches?(env)
        req = @request.new(env)
        ...
        return true
      end

      def call(env)
        matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
      end
      ...
  end
end

Rails Also im Grunde überprüft, abhängig von der env-Hash, wenn irgendwelche Route Streichhölzer. Wenn der so ist es das env Hash an die Anwendung leitet die Antwort zu berechnen, da sonst sofort mit einem 404. So reagiert jeder Webserver, der mit der Zahnstange Schnittstelle Konvention konform ist, ist in der Lage eine voll ausgeblasen Rails Anwendung zu dienen.

Middleware

Rack unterstützt auch die Erstellung von Middleware-Schichten. Sie im Grunde abfangen eine Anforderung, tun Sie etwas mit ihm und weitergeben. Dies ist sehr nützlich für vielseitige Aufgaben.

Lassen Sie uns sagen, dass wir die Protokollierung zu unserem JSON Server hinzufügen möchten, dass auch Maßnahmen, wie lange eine Anfrage dauert. Wir können einfach eine Middleware-Logger erstellen, die dies tut genau das:

class RackLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    @start = Time.now
    @status, @headers, @body = @app.call(env)
    @duration = ((Time.now - @start).to_f * 1000).round(2)

    puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
    [@status, @headers, @body]
  end
end

Wenn es erstellt wird, speichert es sich um eine Kopie des aktuellen Rack-Anwendung. In unserem Fall ist ein Beispiel unserer JSONServer. Rack ruft automatisch die Call-Methode auf der Middleware und erwartet eine [status, headers, body] Array zurück, genau wie unsere JSONServer kehrt zurück.

So in dieser Middleware, der Startpunkt genommen wird, dann der eigentliche Aufruf der JSONServer mit @app.call(env) gemacht, dann dem Logger gibt den Protokolleintrag auf und kehrt schließlich die Antwort als [@status, @headers, @body].

Um unsere kleinen rackup.ru Verwendung dieser Middleware zu machen, fügen Sie eine Anwendung RackLogger es wie folgt aus:

class JSONServer
  def call(env)
    [200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
  end
end

class RackLogger
  def initialize(app)
    @app = app
  end

  def call(env)
    @start = Time.now
    @status, @headers, @body = @app.call(env)
    @duration = ((Time.now - @start).to_f * 1000).round(2)

    puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
    [@status, @headers, @body]
  end
end

use RackLogger

map '/hello.json' do
  run JSONServer.new
end   

Starten Sie den Server und voila, gibt es ein Protokoll auf jeder Anfrage. Rack können Sie mehrere Middle hinzufügen, die in der Reihenfolge aufgerufen werden sie hinzugefügt werden. Es ist nur eine gute Möglichkeit, Funktionen hinzuzufügen, ohne den Kern der Rack-Anwendung zu ändern.

Rack - The Gem

Obwohl Rack - vor allen Dingen - ist eine Konvention, es ist auch ein Juwel, die hohe Funktionalität bietet. Einer von ihnen, dass wir bereits für unsere JSON-Server, der rackup Befehl verwendet. Aber es gibt noch mehr! Das Rack Juwel bietet wenige Anwendungen für viele Anwendungsfälle, wie die Bedienung statische Dateien oder auch ganze Verzeichnisse. Mal sehen, wie wir eine sehr einfache HTML-Datei, die sich auf htmls / index.html eine einfache Datei, zum Beispiel dienen:

<!DOCTYPE HTML>
  <html>
  <head>
    <title>The Index</title>
  </head>

  <body>
    <p>Index Page</p>
  </body>
</html>

Wir wollen vielleicht diese Datei dienenvon der Website root, fügen wir also folgendes zu unserem config.ru:

map '/' do
  run Rack::File.new "htmls/index.html"
end

Wenn wir http://localhost:9292 besuchen wir sehen unsere HTML-Datei perfekt gemacht. Das ist, war einfach, nicht wahr?

Lassen Sie uns ein ganzes Verzeichnis von Javascript-Dateien hinzufügen, indem Sie einige Javascript-Dateien unter / Javascripts Erstellen und Hinzufügen der folgenden zum config.ru:

map '/javascripts' do
  run Rack::Directory.new "javascripts"
end

Starten Sie den Server und Besuch http://localhost:9292/javascript und Sie erhalten eine Liste aller JavaScript-Dateien sehen Sie jetzt gerade von überall aufnehmen können.

Rack-Middleware ist eine Möglichkeit, eine Anfrage und Antwort zu filtern, kommt in Ihre Anwendung. Eine Middleware-Komponente befindet sich zwischen dem Client und dem Server, eingehende Anfragen und Outbound-Antworten verarbeiten, aber es ist mehr als Schnittstelle, die zu reden auf dem Web-Server verwendet werden kann. Es ist zu gruppieren und um Module verwendet, die in der Regel Klassen Rubin sind, und geben Sie die Abhängigkeit zwischen ihnen. Rack-Middleware-Modul muss nur: - gibt Konstruktor, die nächste Anwendung in Stapeln als Parameter übernimmt - reagiert auf „Call“ -Methode, die Umgebung Hash als Parameter annimmt. Wiederkehrwert aus diesem Aufruf ist ein Array von:. Statuscode, Umgebung hash und Reaktionskörper

Ich habe verwendet Rack-Middleware ein paar Probleme zu lösen:

  1. Beschäftigte JSON Parse-Fehler mit benutzerdefinierter Rack-Middleware und schön formatierte Fehlermeldungen, wenn Client sendet JSON gesprengt
  2. Content Compression über Rack-:: Deflater

Es ergibt ziemlich elegant Korrekturen in beiden Fällen.

Was ist Rack-?

Rack stellt eine minimale Schnittstelle zwischen zwischen Webservern Ruby und Ruby-Frameworks unterstützen.

Rack Verwenden Sie können eine Rack-Anwendung schreiben.

Rack wird die Umwelt Hash-Pass (ein Hash, in einer HTTP-Anforderung von einem Client enthalten ist, bestehend aus CGI-like-Header) an dem Rack-Anwendung, die Dinge, die in dieser Hash verwenden kann, zu tun, was es will.

Was ist eine Rack-Anwendung?

Um die Verwendung Ständer, müssen Sie eine ‚App‘ zur Verfügung stellen - ein Objekt, das reagiert auf die #call Methode mit dem Umwelt Hash als Parameter (in der Regel als env definiert). #call muss ein Array von genau drei Werte zurück:

  • Status-Code (zB '200'),
  • Hash von Headers ,
  • Antwort Körper (das muss reagieren auf die Ruby-Methode, each).

Sie können eine Rack-Anwendung schreiben, dass die Renditen ein solches Array - dies an Ihren Kunden geschickt wird wieder durch Rack in einem Antwort (dies tatsächlich sein ein Instanz der Klasse Rack::Response [Klick auf Dokumente gehen]).

Eine sehr einfache Rack-Anwendung:

  • gem install rack
  • Erstellen Sie eine config.ru Datei -. Rack-weiß für diese aussehen

Wir werden eine kleine Rack-Anwendung erstellen, die eine Antwort zurück (eine Instanz von Rack::Response), den Antworttext ist ein Array, das einen String enthält. "Hello, World!"

Wir werden einen lokalen Server mit dem Befehl rackup anwerfen.

Wenn Sie den entsprechenden Port in unserem Browser sind, werden wir „Hallo, Welt!“ Sehen im Ansichtsfenster gerendert.

#./message_app.rb
class MessageApp
  def call(env)
    [200, {}, ['Hello, World!']]
  end
end

#./config.ru
require_relative './message_app'

run MessageApp.new

Feuer auf einem lokalen Server mit rackup und besuchen Sie localhost: ‚Hallo, Welt‘ 9292 und Sie sollten sehen gemacht werden.

Dies ist keine umfassende Erklärung, aber im Wesentlichen, was passiert, ist, dass der Client (Browser) einen HTTP-Request sendet über Ihren lokalen Server, auf Rack und Instantiiert MessageApp Rack und läuft call, vorbei in der Umwelt Hash als Parameter in dem Verfahren (das env Argument).

Rack nimmt den Rückgabewert (das Array) und verwendet es eine Instanz von Rack::Response zu erstellen und sendet diese an den Kunden zurück. Der Browser verwendet Magie drucken ‚Hallo Welt!‘ auf den Bildschirm.

Übrigens, wenn Sie wollen, um zu sehen, was die Umwelt Hash aussieht, nur setzen puts env unter def call(env).

Minimal wie es ist, was du hier geschrieben hast, ist eine Rack-Bewerbung!

eine Rack-Anwendung interagieren mit der Incoming Umwelt Hash zu machen

In unserer kleinen Rack-App können wir mit dem env Hash interagieren (siehe hier für mehr über die Umwelt-Hash).

Wir werden die Möglichkeit für den Benutzer zur Eingabe ihrer eigenen Query-String in der URL implementieren, daher wird diese Zeichenfolge in der HTTP-Anforderung vorhanden sein, als ein Wert, eingekapselt in einer der Schlüssel / Wert-Paare des Umwelt-Hash.

Unser Rack-App zugreifen, den Query-String aus der Umwelt Hash und das zurück an den Client (unseren Browser, in diesem Fall) sendet über den Körper in der Antwort.

Von dem Rack-docs für Umwelt Hash: "QUERY_STRING:?.! Der Teil der URL, die die, wenn überhaupt folgt leer sein, aber ist immer erforderlich"

#./message_app.rb
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end

Nun rackup und Besuch localhost:9292?hello (?hello wobei der Abfrage-String) und Sie sollten ‚Hallo‘ sehen in der vi gemachtewport.

Rack-Middleware

Wir werden:

  • Einsatz ein Stück Rack-Middleware in unsere Code-Basis - eine Klasse: MessageSetter,
  • Umwelt Hash treffen wird diese Klasse zuerst und wird als Parameter übergeben werden: env,
  • MessageSetter eine 'MESSAGE' Schlüssel in die Hash-env, sein Wert ist 'Hello, World!' einfügen, wenn env['QUERY_STRING'] leer ist; env['QUERY_STRING'] wenn nicht,
  • schließlich, es wird wieder @app.call(env) - @app ist die nächste App im 'Stack':. MessageApp

Zuerst wird die 'lange Hand' -Version:

#./middleware/message_setter.rb
class MessageSetter
  def initialize(app)
    @app = app
  end

  def call(env)
    if env['QUERY_STRING'].empty?
      env['MESSAGE'] = 'Hello, World!'
    else
      env['MESSAGE'] = env['QUERY_STRING']
    end
    @app.call(env)
  end
end

#./message_app.rb (same as before)
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end

#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'

app = Rack::Builder.new do
  use MessageSetter
  run MessageApp.new
end

run app

Von der Rack-:: Builder docs sehen wir, dass Rack::Builder Arbeitsgeräte ein kleiner DSL iterativ Konstrukt Rack-Anwendungen. Diese im Grunde bedeutet, dass Sie einen ‚Stapel‘ bauen, bestehend aus einem oder mehreren Middlewares und eine ‚Bottom-Ebene‘ Anwendung bis zum Versand an. Alle Anfragen an Ihrer Bottom-Level-Anwendung durchlaufen werden zunächst durch Ihre Middleware (s) verarbeitet werden.

#use gibt an Middleware in einem Stapel zu verwenden. Es nimmt die Middleware als Argument.

Rack-Middleware muss:

  • hat einen Konstruktor, der die nächste Anwendung in dem Stapel als Parameter annimmt.
  • reagiert auf die call Methode, die die Umwelt Hash als Parameter annimmt.

In unserem Fall die 'Middleware' MessageSetter ist, der 'Konstruktor' ist MessageSetter des initialize Methode, die 'nächste Anwendung' im Stapel MessageApp.

Also hier, wegen dem, was Rack::Builder tut unter der Haube, das app Argument von MessageSetter des initialize Methode ist MessageApp.

(erhalten Ihren Kopf um die oben, bevor er auf)

Daher ist jedes Stück Middleware im Wesentlichen ‚geht nach unten‘ der bestehenden Umgebung Hash auf die nächste Anwendung in der Kette - so haben Sie die Möglichkeit, dass die Umwelt Hash innerhalb der Middleware zu mutieren, bevor es auf die nächste Anwendung in dem Stapel vorbei .

#run nimmt ein Argument, das ein Objekt ist, dass reagiert auf #call und kehrt ein Rack-Response (eine Instanz von Rack::Response).

Schlussfolgerungen

Rack::Builder Verwenden Sie Ketten von Middlewares und jede Anfrage auf Ihre Anwendung erstellen können, bevor schließlich das letzte Stück verarbeitet werden, in dem Stapel von jeder Middleware wiederum verarbeitet werden (in unserem Fall MessageApp). Dies ist äußerst nützlich, weil es verschiedene Stufen der Verarbeitung von Anforderungen trennt-out. In Bezug auf die ‚Trennung von Bedenken‘, ist es nicht viel sauberer sein könnte!

Sie können einen Antrag Pipeline ', bestehend aus mehreren Middlewares konstruieren, die sich mit Dingen wie:

  • Authentication
  • Authorization
  • Caching
  • Dekoration
  • Performance & Usage Überwachung
  • Execution (tatsächlich die Anforderung verarbeiten und eine Antwort liefern)

(oben Aufzählungszeichen aus einer anderen Antwort auf diesen Thread)

Sie werden sehen, dies oft in professionellen Sinatra-Anwendungen. Sinatra Anwendungen Rack-! Siehe hier für die Definition dessen, was Sinatra IS

Als abschließende Bemerkung, unsere config.ru in kurzer Hand Stil geschrieben, produzieren genau die gleiche Funktionalität (und das ist, was Sie in der Regel sehen):

require_relative './message_app'
require_relative './middleware/message_setter'

use MessageSetter
run MessageApp.new

Und mehr explizit zu zeigen, was MessageApp tut, hier ist seine ‚lange Hand‘ -Version, dass explizit zeigt, dass #call eine neue Instanz von Rack::Response schafft, mit den drei Argumenten erforderlich.

class MessageApp
  def call(env)
    Rack::Response.new([env['MESSAGE']], 200, {})
  end
end

Nützliche Links

Rack - Die Schnittstelle S / W-Web & App Server

Rack ist ein Ruby-Paket, das eine Schnittstelle für einen Web-Server mit der Anwendung zu kommunizieren, zur Verfügung stellt. Es ist leicht, Middleware-Komponenten zwischen dem Web-Server hinzuzufügen und der App, die Art und Weise Ihre Anfrage / Antwort verhält sich zu ändern. Die Middleware-Komponente befindet sich zwischen dem Client und dem Server, Verarbeitung der eingehenden Anfragen und ausgehenden Antworten.

In Laien Worten, es ist im Grunde nur eine Reihe von Richtlinien, wie ein Server und eine Rails-Anwendung (oder ein anderes App Ruby-Netz) miteinander sprechen sollten .

Rack verwenden, bietet eine „app“: ein Objekt, das reagiert auf das Anrufverfahren wird die Umgebung hash als Parameter nimmt, und ein Array mit drei Elementen zurückzukehr:

  • Der HTTP-Response-Code
  • Ein Hash des Header
  • Die Antworttext , die jedem Anfrage reagieren muss .

Für weitere Erklärung, können Sie die unten stehenden Links folgen.

1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources

In Schienen haben wir config.ru als Rack-Datei können Sie eine beliebige Rack-Datei mit rackup Befehl ausführen. Und der Standard-Port für diese ist 9292. Um dies zu testen, können Sie einfach rackup im Schienen-Verzeichnis ausgeführt und das Ergebnis sehen. Sie können auch Port, an dem Sie es ausführen möchten zuweisen. Befehl ausgeführt werden soll Rack-Datei auf einem bestimmten Port

rackup -p PORT_NUMBER
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top