Was ist Rack-Middleware?
-
20-09-2019 - |
Frage
Was ist Rack-Middleware in Ruby? Ich konnte keine gute Erklärung finden, was sie unter „Middleware“.
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
-
Der Kommentar dazu ein Weg, um Filteranforderungen zu sein kommt wahrscheinlich aus der Railscast Folge 151 :. Rack-Middleware Screencast
-
Rack-Middleware von Rack-entwickelt und es gibt ein großes Intro unter Einführung in die Rack-Middleware .
-
Es gibt eine Einführung in Middleware auf Wikipedia rel="noreferrer">.
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.
hatte ich ein Problem, mich zu verstehen Gestell für eine gute Zeit. Ich verstand nur voll es nach arbeiten daran, diesem Miniatur-Ruby-Web-Server selbst. Ich habe meine Erkenntnisse über Rack-geteilt (in Form einer Geschichte) hier auf meinem Blog:
Ausführen So ist es klar, dass die Wie bereits erläutert unter: http://guides.rubyonrails.org /rails_on_rack.html#action-dispatcher-middleware-stack , Rails Anwendungen Rack-Middle für viele es die Funktionalität, und Sie können Sie mit Der Vorteil der Funktionalität in einem Middleware-Implementierung ist, dass Sie es auf jedem Rack-Rahmen wieder verwenden können, damit alle wichtigen diejenigen Ruby und nicht nur Rails. config.ru
minimal runnable Beispiel app = Proc.new do |env|
[
200,
{
'Content-Type' => 'text/plain'
},
["main\n"]
]
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
@status, @headers, @body = @app.call(env)
[@status, @headers, @body << "Middleware\n"]
end
end
use(Middleware)
run(app)
rackup
und Besuch localhost:9292
. Die Ausgabe lautet: main
Middleware
Middleware
Wraps und den Haupt-App aufrufen. Daher ist es in der Lage die Anforderung, und Post-Prozess die Antwort in irgendeiner Weise vorverarbeitet. config.middleware.use
Familie Methoden besitzen auch hinzufügen.
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:
- Beschäftigte JSON Parse-Fehler mit benutzerdefinierter Rack-Middleware und schön formatierte Fehlermeldungen, wenn Client sendet JSON gesprengt
- 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, wennenv['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