Domanda

Cos'è il middleware Rack in Ruby?Non sono riuscito a trovare alcuna buona spiegazione per cosa intendono per "middleware".

È stato utile?

Soluzione

Rack come design

Il middleware rack è più di "un modo per filtrare una richiesta e una risposta": è un'implementazione del modello di progettazione della pipeline per i server Web che utilizzano Cremagliera.

Separa in modo molto netto le diverse fasi dell'elaborazione di una richiesta: la separazione delle preoccupazioni è un obiettivo chiave di tutti i prodotti software ben progettati.

Ad esempio con Rack posso avere fasi separate della pipeline facendo:

  • Autenticazione:quando arriva la richiesta i dati di accesso degli utenti sono corretti?Come posso convalidare questo OAuth, l'autenticazione di base HTTP, nome/password?

  • Autorizzazione:"l'utente è autorizzato a eseguire questa particolare attività?", ovverosicurezza basata sui ruoli.

  • Memorizzazione nella cache:ho già elaborato questa richiesta, posso restituire un risultato memorizzato nella cache?

  • Decorazione:come posso migliorare la richiesta per migliorare l'elaborazione a valle?

  • Monitoraggio delle prestazioni e dell'utilizzo:quali statistiche posso ottenere dalla richiesta e dalla risposta?

  • Esecuzione:gestire effettivamente la richiesta e fornire una risposta.

Essere in grado di separare le diverse fasi (e facoltativamente includerle) è di grande aiuto nello sviluppo di applicazioni ben strutturate.

Comunità

Esiste anche un ottimo ecosistema in via di sviluppo attorno al Rack Middleware: dovresti essere in grado di trovare componenti rack precostruiti per eseguire tutti i passaggi precedenti e altro ancora.Vedere il wiki di Rack GitHub per un elenco di middleware.

Cos'è il middleware?

Middleware è un termine terribile che si riferisce a qualsiasi componente/libreria software che assiste ma non è direttamente coinvolto nell'esecuzione di alcune attività.Esempi molto comuni sono la registrazione, l'autenticazione e altro componenti di elaborazione comuni e orizzontali.Queste tendono ad essere le cose di cui tutti hanno bisogno in più applicazioni, ma non molte persone sono interessate (o dovrebbero essere) a costruire se stesse.

Maggiori informazioni

Altri suggerimenti

Prima di tutto, Rack è esattamente due cose:

  • Una convenzione un'interfaccia server web
  • Un gioiello

Rack - Il Webserver interfaccia

Le fondamenta stesse della cremagliera è una semplice convenzione. Ogni rastrelliera webserver compatibile sarà sempre chiamare una chiamata di metodo su un oggetto gli dai e servire il risultato di tale metodo. Rack specifica esattamente come questo metodo di chiamata deve essere simile, e che cosa ha di tornare. Questo è il rack.

Diamogli una semplice prova. Userò WEBrick come cremagliera webserver conforme, ma nessuno di loro farà. Creiamo una semplice applicazione web che restituisce una stringa JSON. Per questo creiamo un file chiamato config.ru. Il config.ru verrà automaticamente chiamato dal comando rackup della gemma cremagliera che si limiterà a eseguire il contenuto della config.ru in un web server rack compliant. Quindi cerchiamo di aggiungere il seguente al file config.ru:

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

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

La convenzione precisa il nostro server ha un metodo chiamato chiamata che accetta un hash ambiente e restituisce un array con la forma [stato, intestazioni, corpo] per il webserver di servire. Proviamo fuori semplicemente chiamando rackup. Un server compatibile cremagliera di default, forse WEBrick o Mongrel inizierà immediatamente e attendere che le richieste per servire.

$ 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

Testiamo il nostro nuovo server JSON da un'arricciatura o visitando il http://localhost:9292/hello.json url e voilà:

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

Funziona. Grande! Questa è la base di ogni framework web, sia esso rotaie o Sinatra. Ad un certo punto attuano una chiamata di metodo, lavorare attraverso tutto il codice del framework, e, infine, restituire una risposta nella tipica [stato, intestazioni, corpo] modulo.

In Ruby on Rails ad esempio le richieste del rack colpisce la classe ActionDispatch::Routing.Mapper che assomiglia a questo:

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

Quindi, fondamentalmente Rails controlli, dipendenti dalla hash ENV se eventuali partite di percorso. Se è così passa l'hash ENV sul all'applicazione per calcolare la risposta, altrimenti risponde immediatamente con un 404. Quindi, qualsiasi server web che è compatibile con la convenzione di interfaccia rack, è in grado di servire un'applicazione Rails completamente bruciato.

Middleware

Rack supporta anche la creazione di strati di middleware. In sostanza intercettare una richiesta, fare qualcosa con esso e trasmetterla. Questo è molto utile per i compiti versatili.

Diciamo che vogliamo aggiungere la registrazione al nostro server JSON che misura anche quanto tempo una richiesta prende. Possiamo semplicemente creare un logger middleware che fa esattamente questo:

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

Quando viene creato, si salva una copia della domanda effettiva rack. Nel nostro caso questo è un'istanza della nostra JSONServer. chiama Rack automaticamente il metodo chiamata sul middleware e si aspetta di nuovo una matrice [status, headers, body], proprio come i nostri rendimenti JSONServer.

Quindi, in questo middleware, viene preso il punto di inizio, poi la chiamata effettiva al JSONServer è fatto con @app.call(env), quindi il registratore emette la voce di registrazione e, infine, restituisce la risposta come [@status, @headers, @body].

Per rendere il nostro piccolo rackup.ru utilizzare questo middleware, aggiungere un RackLogger uso in questo modo:

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   

Riavviare il server e voilà, si emette un registro a ogni richiesta. Rack permette di aggiungere più middleware che vengono chiamati nell'ordine in cui vengono aggiunti. E 'solo un ottimo modo per aggiungere funzionalità senza cambiare il cuore dell'applicazione cremagliera.

Rack - The Gem

Anche se cremagliera - prima di tutto - è una convenzione è anche un gioiello che offre grande funzionalità. Uno di loro abbiamo già usato per il nostro server JSON, il comando rackup. Ma c'è di più! La gemma cremagliera fornisce piccole applicazioni per un sacco di casi d'uso, come servire i file statici o anche intere directory. Vediamo come serviamo un semplice file, ad esempio un file HTML molto di base situato a HTMLs / index.html:

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

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

forse vogliamo servire questo filedalla radice sito, quindi cerchiamo di aggiungere il seguente al nostro config.ru:

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

Se visitiamo http://localhost:9292 vediamo il nostro file html perfettamente reso. Quella era facile, giusto?

Aggiungiamo un intera directory di file JavaScript con la creazione di alcuni file javascript sotto / javascript e aggiungendo quanto segue alla config.ru:

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

Riavviare il server e visita http://localhost:9292/javascript e vedrete una lista di tutti i file JavaScript è possibile includere ora direttamente da nessuna parte.

Ho avuto un problema capire me stesso rack per una buona quantità di tempo. Ho solo completamente capito, dopo aver lavorato per rendere questo miniatura server web Rubino me stesso. Ho condiviso i miei insegnamenti su rack (sotto forma di una storia) qui sul mio blog: http://gauravchande.com/what-is-rack-in-ruby-rails

Il feedback è più che benvenuto.

config.ru minimo esempio eseguibile

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)

Esegui rackup e visitare localhost:9292. L'output è:

main
Middleware

Quindi, è chiaro che le coperture Middleware e richiama l'applicazione principale. Pertanto è in grado di pre-elaborare la richiesta, e post-processo la risposta in alcun modo.

Come spiegato in: http://guides.rubyonrails.org /rails_on_rack.html#action-dispatcher-middleware-stack , Rails usa middleware rack per un sacco di sue funzionalità, ed è possibile aggiungere il proprio troppo con i metodi di famiglia config.middleware.use.

Il vantaggio di implementare funzionalità in un middleware è che si può riutilizzare in qualsiasi quadro Rack, quindi tutti i più importanti quelli di Ruby, e non solo Rails.

Il middleware rack è un modo per filtrare una richiesta e una risposta in arrivo nella tua applicazione.Un componente middleware si trova tra il client e il server, elaborando le richieste in entrata e le risposte in uscita, ma è più di un'interfaccia che può essere utilizzata per comunicare con il server web.Viene utilizzato per raggruppare e ordinare i moduli, che di solito sono classi Ruby, e specificare la dipendenza tra loro.Il modulo middleware rack deve solo:– avere un costruttore che accetta come parametro l'applicazione successiva nello stack – rispondere al metodo “call”, che accetta l'hash dell'ambiente come parametro.Il valore restituito da questa chiamata è un array di:codice di stato, hash dell'ambiente e corpo della risposta.

Ho usato il middleware rack per risolvere un paio di problemi:

  1. Facendo errori di analisi JSON con Rack personalizzato middleware e il ritorno dei messaggi di errore ben formattati quando client invia sballato JSON
  2. Compressione contenuti tramite Rack :: Deflater

E 'concessa correzioni abbastanza elegante in entrambi i casi.

Cos'è Rack?

Rack fornisce un'interfaccia minima tra i server web che supportano Ruby e i framework Ruby.

Usando Rack puoi scrivere un'applicazione Rack.

Rack passerà l'hash dell'ambiente (un hash, contenuto all'interno di una richiesta HTTP da un client, costituito da intestazioni simili a CGI) alla tua applicazione Rack che può utilizzare le cose contenute in questo hash per fare ciò che vuole.

Cos'è un'applicazione rack?

Per utilizzare Rack, devi fornire un'"app", un oggetto che risponde al #call metodo con l'hash dell'ambiente come parametro (tipicamente definito come env). #call deve restituire un Array di esattamente tre valori:

  • IL Codice di stato (ad esempio "200"),
  • UN Hash delle intestazioni,
  • IL Corpo di risposta (che deve rispondere al metodo Ruby, each).

Puoi scrivere un'applicazione Rack che restituisca tale array: questo verrà rispedito al tuo client, da Rack, all'interno di un file Risposta (questo sarà in realtà un esempio della Classe Rack::Response [clicca per andare alla documentazione]).

Un'applicazione rack molto semplice:

  • gem install rack
  • Creare un config.ru file - Rack sa come cercarlo.

Creeremo una piccola applicazione rack che restituisce una risposta (un'istanza di Rack::Response) il cui corpo della risposta è un array che contiene una stringa: "Hello, World!".

Avviamo un server locale utilizzando il comando rackup.

Quando visitiamo la porta pertinente nel nostro browser vedremo "Ciao, mondo!" reso nel punto di vista.

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

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

run MessageApp.new

Avvia un server locale con rackup e visita host locale:9292 e si dovrebbe vedere 'Ciao, mondo!' reso.

Questa non è una spiegazione esaustiva, ma essenzialmente ciò che accade qui è che il Client (il browser) invia una richiesta HTTP al Rack, tramite il server locale, e Rack istanzia MessageApp e corre call, passando l'hash dell'ambiente come parametro nel metodo (the env discussione).

Rack prende il valore restituito (l'array) e lo utilizza per creare un'istanza di Rack::Response e lo rispedisce al Cliente.Il browser utilizza Magia per stampare 'Hello, World!' sullo schermo.

Per inciso, se vuoi vedere come appare l'hash dell'ambiente, metti semplicemente puts env sotto def call(env).

Per quanto minimale, ciò che hai scritto qui è un'applicazione Rack!

Far interagire un'applicazione rack con l'hash dell'ambiente in entrata

Nella nostra piccola app Rack, possiamo interagire con env hash (vedi Qui per ulteriori informazioni sull'hash dell'ambiente).

Implementeremo la possibilità per l'utente di inserire la propria stringa di query nell'URL, quindi quella stringa sarà presente nella richiesta HTTP, incapsulata come valore in una delle coppie chiave/valore dell'hash dell'ambiente.

La nostra app Rack accederà a quella stringa di query dall'hash dell'ambiente e la invierà al client (il nostro browser, in questo caso) tramite il corpo nella risposta.

Dai documenti Rack sull'hash dell'ambiente:"STRINGA DELLA DOMANDA:La parte dell'URL della richiesta che segue ?, se presente.Può essere vuoto, ma è sempre obbligatorio!"

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

Ora, rackup e visita localhost:9292?hello (?hello essendo la stringa di query) e dovresti vedere "ciao" visualizzato nel viewport.

Middleware per rack

Noi:

  • inserisci un pezzo di Rack Middleware nella nostra base di codice: una classe: MessageSetter,
  • l'hash dell'ambiente colpirà per primo questa classe e verrà passato come parametro: env,
  • MessageSetter inserirà a 'MESSAGE' chiave nell'hash env, il cui valore è 'Hello, World!' Se env['QUERY_STRING'] è vuoto; env['QUERY_STRING'] altrimenti,
  • infine, ritornerà @app.call(env) - @app essendo la prossima app nello 'Stack': MessageApp.

Innanzitutto, la versione "a mano lunga":

#./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

Dal Rack::Builder documenti Lo vediamo Rack::Builder implementa un piccolo DSL per costruire in modo iterativo applicazioni Rack.Ciò significa sostanzialmente che è possibile creare uno "stack" costituito da uno o più middleware e un'applicazione di "livello inferiore" a cui inviare.Tutte le richieste che passano alla tua applicazione di livello inferiore verranno prima elaborate dai tuoi Middleware.

#use specifica il middleware da utilizzare in uno stack.Prende il middleware come argomento.

Il middleware rack deve:

  • avere un costruttore che accetta come parametro l'applicazione successiva nello stack.
  • rispondere al call metodo che accetta l'hash dell'ambiente come parametro.

Nel nostro caso, il "Middleware" lo è MessageSetter, il "costruttore" è di MessageSetter initialize metodo, la "prossima applicazione" nello stack è MessageApp.

Quindi ecco, a causa di cosa Rack::Builder fa sotto il cofano, il app argomento di MessageSetter'S initialize il metodo è MessageApp.

(pensa a quanto sopra prima di andare avanti)

Pertanto, ogni parte del middleware essenzialmente "trasmette" l'hash dell'ambiente esistente all'applicazione successiva nella catena, in modo da avere l'opportunità di modificare l'hash dell'ambiente all'interno del middleware prima di trasmetterlo all'applicazione successiva nello stack.

#run accetta un argomento che è un oggetto a cui risponde #call e restituisce una risposta del rack (un'istanza di Rack::Response).

Conclusioni

Utilizzando Rack::Builder puoi costruire catene di Middleware e qualsiasi richiesta alla tua applicazione verrà elaborata a turno da ciascun Middleware prima di essere finalmente elaborata dal pezzo finale nello stack (nel nostro caso, MessageApp).Ciò è estremamente utile perché separa le diverse fasi di elaborazione delle richieste.In termini di “separazione degli interessi”, non potrebbe essere molto più pulito!

È possibile costruire una "pipeline di richiesta" composta da diversi middleware che si occupano di cose come:

  • Autenticazione
  • Autorizzazione
  • Memorizzazione nella cache
  • Decorazione
  • Monitoraggio delle prestazioni e dell'utilizzo
  • Esecuzione (gestire effettivamente la richiesta e fornire una risposta)

(sopra i punti elenco da un'altra risposta su questo thread)

Lo vedrai spesso nelle applicazioni professionali Sinatra.Sinatra usa Rack!Vedere Qui per la definizione di ciò che Sinatra È!

Come ultima nota, il ns config.ru può essere scritto in uno stile abbreviato, producendo esattamente la stessa funzionalità (e questo è ciò che vedrai in genere):

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

use MessageSetter
run MessageApp.new

E per mostrare più esplicitamente di cosa MessageApp sta facendo, ecco la sua versione "lunga" che lo dimostra esplicitamente #call sta creando una nuova istanza di Rack::Response, con i tre argomenti richiesti.

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

Link utili

Rack: l'interfaccia in bianco e nero del server Web e app

Rack è un pacchetto Ruby che fornisce un'interfaccia per un server web per comunicare con l'applicazione.È facile aggiungere componenti middleware tra il server Web e l'app per modificare il modo in cui si comporta la richiesta/risposta.Il componente middleware si trova tra il client e il server, elaborando le richieste in entrata e le risposte in uscita.

In parole povere, si tratta fondamentalmente solo di una serie di linee guida su come un server e un'app Rails (o qualsiasi altra app web Ruby) dovrebbero comunicare tra loro.

Per utilizzare Rack, fornire una "app":un oggetto che risponde alla chiamata del metodo, prendendo come parametro l'hash dell'ambiente e restituendo un Array con tre elementi:

  • Il codice di risposta HTTP
  • Un hash di intestazioni
  • IL corpo della risposta, che deve rispondere a ciascuno richiesta.

Per ulteriori spiegazioni, è possibile seguire i collegamenti seguenti.

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 rails, abbiamo config.ru come file rack, con cui puoi eseguire qualsiasi file rack rackup comando.E la porta predefinita per questo è 9292.Per testarlo, puoi semplicemente correre rackup nella tua directory rails e guarda il risultato.Puoi anche assegnare la porta su cui desideri eseguirlo.Il comando per eseguire il file rack su qualsiasi porta specifica è

rackup -p PORT_NUMBER
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top