Co to jest Rack middleware?
Czym jest Rack middleware w Ruby? Nie mogłem znaleźć dobrego wytłumaczenia, co mają na myśli przez "middleware".
9 answers
Regał jako konstrukcja
Rack middleware jest czymś więcej niż "sposobem filtrowania żądania i odpowiedzi" - jest to implementacja pipeline design pattern dla serwerów WWW korzystających z Rack.
Bardzo czysto oddziela poszczególne etapy przetwarzania wniosku - rozdzielenie problemów jest kluczowym celem wszystkich dobrze zaprojektowanych produktów.
Na przykład z Rackiem mogę mieć oddzielne etapy rurociągu robi:
Uwierzytelnianie: czy po otrzymaniu żądania dane logowania użytkowników są poprawne? Jak zweryfikować OAuth, podstawowe uwierzytelnianie HTTP, nazwę / hasło?
Autoryzacja : "czy użytkownik jest upoważniony do wykonywania tego konkretnego zadania?", czyli bezpieczeństwo oparte na rolach.
Buforowanie: czy przetworzyłem już to żądanie, Czy Mogę zwrócić wynik buforowany?
Dekoracja : Jak mogę ulepszyć żądanie, aby usprawnić przetwarzanie na późniejszym etapie?
Monitorowanie wydajności i użycia: jakie statystyki mogę uzyskać z żądania i odpowiedzi?
Wykonanie: faktycznie obsłużyć żądanie i dostarczyć odpowiedź.
Możliwość oddzielenia poszczególnych etapów (i opcjonalnie ich włączenia) jest bardzo pomocna w tworzeniu dobrze ustrukturyzowanych aplikacji.
Społeczność
Jest też świetny eko-system rozwój wokół oprogramowania pośredniego Rack - powinieneś być w stanie znaleźć wstępnie zbudowane komponenty rack, aby wykonać wszystkie powyższe kroki i więcej. Zobacz Rack GitHub wiki, aby uzyskać listę middleware .Co To jest Middleware?
[2]}Middleware jest strasznym terminem, który odnosi się do każdego komponentu oprogramowania/biblioteki, która pomaga, ale nie jest bezpośrednio zaangażowana w wykonanie jakiegoś zadania. Bardzo powszechnymi przykładami są rejestrowanie, uwierzytelnianie i inne wspólne, poziome przetwarzanie komponenty . Są to zazwyczaj rzeczy, których każdy potrzebuje w wielu aplikacjach, ale nie zbyt wiele osób jest zainteresowanych (lub powinno) budowaniem siebie.Więcej Informacji
Komentarz o tym, że jest to sposób na filtrowanie zapytań pochodzi prawdopodobnie z RailsCast episode 151: Rack Middleware screen cast.
Rack middleware wyewoluowało z Racka i jest świetne intro na Wprowadzenie do racka middleware .
Na Wikipedii jest wstęp do middleware tutaj.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-09 01:58:08
Po pierwsze, Rack to dokładnie dwie rzeczy:
- a webserver interface convention
- klejnot
Rack-Interfejs Serwera Www
Podstawy racka to prosta konwencja. Każdy serwer WWW zgodny z rack zawsze wywoła metodę call na danym obiekcie i poda wynik tej metody. Rack określa dokładnie, jak ma wyglądać ta metoda wywołania i co ma zwrócić. To rack.Let ' s give it prosta próba. Użyję WEBrick jako serwera zgodnego z rackiem, ale każdy z nich to zrobi. Stwórzmy prostą aplikację webową, która zwraca ciąg JSON. W tym celu utworzymy plik o nazwie config.ru. The config.ru zostanie automatycznie wywołana przez polecenie rackup gem, które po prostu uruchomi zawartość config.ru w serwerze zgodnym z rackiem. Dodajmy więc do config.ru plik:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
Zgodnie z konwencją nasz serwer posiada metodę call, która akceptuje hash środowiska i zwraca tablicę z formą [status, headers, body] dla serwera WWW, który ma służyć. Wypróbujmy go, po prostu dzwoniąc do rackup. Domyślny serwer zgodny z rackiem, może WEBrick lub Kundel uruchomi się i natychmiast czeka na serwowanie żądań.
$ 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
Przetestujmy nasz nowy serwer JSON przez curling lub odwiedzając url http://localhost:9292/hello.json
i voila:
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
To działa. Świetnie! To podstawa każdego frameworka internetowego, czy to Rails czy Sinatra. W pewnym momencie zaimplementuj metodę wywołania, przejrzyj cały kod frameworka i na koniec zwróć odpowiedź w typowej formie [status, nagłówki, body].
W Ruby on Rails na przykład żądania rack trafiają do klasy ActionDispatch::Routing.Mapper
, która wygląda tak:
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
Więc w zasadzie sprawdzanie szyn, zależne od skrótu env, jeśli jakaś trasa pasuje. Jeśli tak, to przekazuje hash env do aplikacji, aby obliczyć odpowiedź, w przeciwnym razie natychmiast odpowiada za pomocą 404. Więc każdy serwer WWW, który jest zgodny dzięki konwencji interfejsu rack jest w stanie obsłużyć w pełni wydmuchaną aplikację szyn.
Middleware
Rack wspiera również tworzenie warstw middleware. Zasadniczo przechwytywają żądanie, robią coś z nim i przekazują dalej. Jest to bardzo przydatne do wszechstronnych zadań.
Załóżmy, że chcemy dodać logowanie do naszego serwera JSON, który również mierzy, jak długo trwa żądanie. Możemy po prostu stworzyć middleware logger, który robi dokładnie to:
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
Kiedy zostanie utworzona, zapisuje sobie kopię rzeczywistej aplikacji rack. W naszym przypadku jest to przykład naszego serwera JSONServer. Rack automatycznie wywołuje metodę call na serwerze middleware i oczekuje z powrotem tablicy [status, headers, body]
, tak jak zwraca nasz jsonserver.
Tak więc w tym oprogramowaniu pośredniczącym pobierany jest punkt początkowy, następnie wykonywane jest rzeczywiste wywołanie serwera JSONServer za pomocą @app.call(env)
, następnie logger wypisuje wpis logowania i na koniec zwraca odpowiedź jako [@status, @headers, @body]
.
Aby nasze małe rackup.ru użyj tego middleware, Dodaj do niego use RackLogger w następujący sposób:
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
Uruchom ponownie serwer i voila, wyświetla log na każde żądanie. Rack pozwala na dodawanie wielu middlewares, które są wywoływane w kolejności, w jakiej są dodawane. To po prostu świetny sposób na dodanie funkcjonalności bez zmiany rdzenia aplikacji rack.
Regał-Klejnot
Chociaż rack-przede wszystkim-jest konwencją, to jest również klejnotem, który zapewnia dużą funkcjonalność. Jeden z nich, którego użyliśmy już na naszym serwerze JSON, Komenda rackup. Ale to nie wszystko! Rack gem zapewnia niewiele aplikacji do wielu zastosowań, takich jak serwowanie plików statycznych lub nawet całych katalogów. Zobaczmy, jak obsługujemy prosty plik, na przykład bardzo podstawowy plik HTML znajdujący się w htmls / index.html:<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
Być może chcemy podać ten plik z katalogu głównego, więc dodajmy do naszego config.ru:
map '/' do
run Rack::File.new "htmls/index.html"
end
Jeśli odwiedzimy http://localhost:9292
widzimy nasz plik html doskonale renderowany. To było łatwe, prawda?
Dodajmy cały katalog plików javascript, tworząc kilka plików javascript w / javascripts i dodając do config.ru:
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
Uruchom ponownie serwer i odwiedź http://localhost:9292/javascript
, a zobaczysz listę wszystkich plików javascript, które możesz teraz dołączyć prosto z dowolnego miejsca.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-24 08:22:25
Miałem problem ze zrozumieniem Racka przez długi czas. W pełni zrozumiałem to dopiero po pracy nad stworzeniem tego miniaturowego serwera Ruby web server. Podzieliłam się swoimi spostrzeżeniami na temat Racka (w formie opowiadania) tutaj na moim blogu: http://gauravchande.com/what-is-rack-in-ruby-rails
Opinie są mile widziane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-15 16:42:14
config.ru
minimal runnable przykład
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)
Uruchom rackup
i odwiedź localhost:9292
. Wyjście to:
main
Middleware
Jest więc jasne, że Middleware
zawija i wywołuje główną aplikację. Dlatego jest w stanie wstępnie przetworzyć żądanie i przetworzyć odpowiedź w dowolny sposób.
Jak wyjaśniono w: http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack , Rails używa Rack middlewares do wielu swoich funkcjonalności, a Ty możesz też dodać własne z config.middleware.use
metodami rodzinnymi.
Zaletą implementacji funkcjonalności w middleware jest to, że można ją ponownie wykorzystać na dowolnym frameworku Rack, a więc wszystkich głównych frameworkach Ruby, a nie tylko Rails.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-28 20:23:52
Co to jest Rack?
Rack zapewnia minimalny interfejs pomiędzy webserwerami obsługującymi frameworki Ruby i Ruby.
Używając Rack możesz napisać aplikację Rack.
Rack przekazuje Hash środowiskowy (Hash, zawarty w żądaniu HTTP od klienta, składający się z nagłówków podobnych do CGI) do aplikacji Rack, która może używać rzeczy zawartych w tym hashu do robienia czego chce.
Co to jest aplikacja Rack?
Aby korzystać z Rack, musisz dostarcza " app " - obiekt, który reaguje na metodę #call
z parametrem hash środowiska (Zwykle zdefiniowanym jako env
). #call
musi zwrócić tablicę dokładnie trzech wartości:
- kod statusu (np. '200'),
- a Hash nagłówków ,
- ciało odpowiedzi (które musi odpowiadać metodzie Ruby,
each
).
Możesz napisać aplikację Rack, która zwróci taką tablicę - zostanie ona odesłana do twojego klienta, przez Rack, wewnątrz odpowiedź (będzie to rzeczywiście instancja klasy Rack::Response
[Kliknij, aby przejść do dokumentów]).
Bardzo Prosta Aplikacja Stojaka:
gem install rack
- Utwórz
config.ru
plik-Rack wie, aby tego szukać.
Utworzymy małą aplikację Rack, która zwróci odpowiedź (instancję Rack::Response
), której ciałem odpowiedzi jest tablica zawierająca Łańcuch znaków: "Hello, World!"
.
Odpalimy serwer lokalny używając polecenie rackup
.
Odwiedzając odpowiedni port w naszej przeglądarce zobaczymy "Hello, World!"rendered in the viewport.
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
Uruchom serwer lokalny z rackup
i odwiedź localhost: 9292 i powinieneś zobaczyć 'Hello, World!"rendered.
MessageApp
i uruchamia call
, przekazywanie w środowisku Hasha jako parametru do metody (argument env
).
Rack pobiera zwracaną wartość (tablicę) i używa jej do utworzenia instancji Rack::Response
i wysyła ją z powrotem do Klienta. Przeglądarka używa magii aby wydrukować 'Hello, World! na ekran.
Nawiasem mówiąc, jeśli chcesz zobaczyć, jak wygląda hash środowiska, po prostu umieść puts env
Pod def call(env)
.
Minimal jak to jest, to co napisałeś tutaj jest Rack podanie!
Dzięki temu, że aplikacja Rack wchodzi w interakcję z przychodzącym środowiskiem hash]}W naszej małej aplikacji Rack, możemy wchodzić w interakcje z env
hash (zobacz tutaj aby dowiedzieć się więcej o hash środowiska).
Zaimplementujemy możliwość wprowadzania przez użytkownika własnego ciągu zapytania do adresu URL, stąd ten łańcuch będzie obecny w żądaniu HTTP, zamkniętego jako wartość w jednej z par klucz / wartość haszu środowiska.
Nasza aplikacja Rack będzie uzyskaj dostęp do tego łańcucha zapytania z hash środowiska i wyślij go z powrotem do Klienta (w tym przypadku naszej przeglądarki) za pośrednictwem treści w odpowiedzi.
From the Rack docs on the Environment Hash: " QUERY_STRING: część adresu URL żądania, która następuje po ? jeśli w ogóle. Może być pusty, ale jest zawsze wymagane!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
Teraz, rackup
i odwiedzić localhost:9292?hello
(?hello
jako ciąg zapytania) i powinieneś zobaczyć "hello" renderowane w viewport.
Rack Middleware
Będziemy:
- włóż kawałek Rack Middleware do naszej bazy kodowej-klasy:
MessageSetter
, - Environment hash trafi najpierw do tej klasy i zostanie przekazany jako parametr:
env
, -
MessageSetter
wstawi'MESSAGE'
klucz do skrótu env, jego wartość wynosi'Hello, World!'
jeślienv['QUERY_STRING']
jest pusta;env['QUERY_STRING']
jeśli nie, - w końcu powróci
@app.call(env)
-@app
jako następna aplikacja w "stosie":MessageApp
.
Po pierwsze, "Długa Ręka" wersja:
#./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
Z Rack::Builder docs widzimy, że Rack::Builder
implementuje mały DSL do iteratywnego konstruowania aplikacji Rack. Oznacza to w zasadzie, że można zbudować "stos" składający się z jednego lub więcej Middlewares i "dolny poziom" aplikacji do wysyłki. Wszystkie wnioski trafiające do aplikacji niższego poziomu będą najpierw przetwarzane przez oprogramowanie pośredniczące.
#use
określa oprogramowanie pośredniczące do użycia w stosie. Przyjmuje middleware jako kłótnia.
Rack Middleware musi:
- mieć konstruktor, który jako parametr przyjmuje następną aplikację w stosie.
- odpowiedź na metodę
call
, która przyjmuje jako parametr hash środowiska.
W naszym przypadku 'Middleware' to MessageSetter
, 'konstruktor' to metoda MessageSetter initialize
, 'Następna aplikacja' w stosie to MessageApp
.
Więc tutaj, z powodu tego, co Rack::Builder
robi pod maską, app
argument MessageSetter
'S initialize
metoda jest MessageApp
.
(zapoznaj się z powyższym, zanim przejdziesz dalej)
Dlatego każdy element oprogramowania pośredniczącego zasadniczo "przekazuje" istniejący hash środowiska do następnej aplikacji w łańcuchu - więc masz możliwość mutacji tego hasha środowiska wewnątrz oprogramowania pośredniczącego przed przekazaniem go do następnej aplikacji w stosie.
#run
pobiera argument, który jest obiektem, który odpowiada na #call
i zwraca odpowiedź Rack (an instancja Rack::Response
).
Wnioski
Za pomocą Rack::Builder
możesz konstruować łańcuchy elementów pośredniczących, a każde żądanie do Twojej aplikacji będzie przetwarzane przez każdą z nich, zanim ostatecznie zostanie przetworzone przez ostatni element w stosie(w naszym przypadku MessageApp
). Jest to niezwykle przydatne, ponieważ oddziela różne etapy przetwarzania żądań. Jeśli chodzi o "oddzielenie obaw", nie może być dużo czystsze!
Można skonstruować 'request pipeline' składający się z kilku pośredników, które zajmują się takimi rzeczami jak:
- uwierzytelnianie
- autoryzacja
- buforowanie
- Dekoracja [[69]}Monitorowanie Wydajności I Użytkowania [72]} W związku z tym, że nie jest to możliwe, nie jest to możliwe.]}
(powyżej punkty z innej odpowiedzi w tym wątku)
Często zobaczysz to w profesjonalnych aplikacjach Sinatra. Sinatra używa Racka! Zobacz tutaj po definicję tego, co Sinatra na!
Na koniec, naszconfig.ru
może być napisany w stylu krótkiej ręki, produkując dokładnie tę samą funkcjonalność (i to jest to, co zwykle zobaczysz):]}
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
I aby dokładniej pokazać, co robi MessageApp
, Oto jego wersja "long-hand", która wyraźnie pokazuje, że #call
tworzy nową instancję Rack::Response
, z wymaganymi trzema argumentami.
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
Przydatne linki
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-12 01:15:51
Rack middleware to sposób na filtrowanie żądania i odpowiedzi pojawiających się w Twojej aplikacji. Komponent middleware znajduje się między Klientem a serwerem, przetwarzając przychodzące żądania i wychodzące odpowiedzi, ale to coś więcej niż interfejs, który może być używany do rozmowy z serwerem WWW. Służy do grupowania i porządkowania modułów, które są zazwyczaj klasami Ruby, oraz określania zależności między nimi. Moduł Rack middleware musi: - posiadać konstruktor, który jako parametr przyjmuje następną aplikację w stosie – odpowiedź na metodę "call", która jako parametr przyjmuje hash środowiska. Zwracaną wartością z tego wywołania jest tablica: kod stanu, hash środowiska i ciało odpowiedzi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-11 14:41:30
Użyłem Rack middleware do rozwiązania kilku problemów:
- Przechwytywanie błędów JSON parse za pomocą niestandardowego oprogramowania pośredniczącego Rack i zwracanie ładnie sformatowanych komunikatów o błędach, gdy Klient przesyła uszkodzony JSON
- Kompresja treści poprzez Rack:: Deflater
W obu przypadkach było to dość eleganckie rozwiązanie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-12 18:17:09
Rack jest klejnotem, który zapewnia prosty interfejs do abstrakcyjnego żądania/odpowiedzi HTTP. Rack znajduje się pomiędzy frameworkami sieciowymi (Rails, Sinatra itp.) a serwerami sieciowymi (unicorn, puma) jako adapter. Z powyższego obrazu to sprawia, że unicorn server jest całkowicie niezależny od wiedzy o rails, a rails nie wie o unicorn. Jest to dobry przykład luźnego sprzężenia, rozdzielenie obaw .
Powyższy obrazek pochodzi z tej konferencji rails na racku https://youtu.be/3PnUV9QzB0g zalecam oglądanie go dla głębszego zrozumienia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-17 18:38:55
Rack-interfejs b / W Web & App Server
Rack jest pakietem Ruby, który zapewnia interfejs dla serwera www do komunikacji z aplikacją. Łatwo jest dodać komponenty middleware między serwerem WWW a aplikacją, aby zmodyfikować sposób zachowania żądania / odpowiedzi. Komponent middleware znajduje się między Klientem a serwerem, przetwarzając przychodzące żądania i wychodzące odpowiedzi.
W słowach laika, jest to w zasadzie tylko zestaw wytycznych, jak serwer i aplikacja Rails (lub jakakolwiek inna aplikacja Ruby) powinny ze sobą rozmawiać.
Aby użyć Rack, podaj "app": obiekt, który reaguje na metodę call, biorąc hash środowiska jako parametr i zwracając tablicę z trzema elementami:
- Kod odpowiedzi HTTP
- Hash nagłówków
- ciało odpowiedzi , które musi odpowiedzieć na każde żądanie .
Aby uzyskać więcej wyjaśnień, możesz postępować zgodnie z poniżej linki.
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
W rails mamy config.ru jako plik rack możesz uruchomić dowolny plik rack za pomocą polecenia rackup
. Domyślnym portem jest 9292
. Aby to przetestować, możesz po prostu uruchomić rackup
w katalogu rails i zobaczyć wynik. Możesz również przypisać port, na którym chcesz go uruchomić. Polecenie uruchamiające plik rack na dowolnym porcie to
rackup -p PORT_NUMBER
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-10 12:22:24