Question

Qu'est-ce que le middleware Rack en Ruby? Je ne pouvais trouver aucune bonne explication pour ce qu'ils veulent dire par « middleware ».

Était-ce utile?

La solution

rack en design

middleware Rack est plus « un moyen de filtrer une demande et la réponse » - il est une implémentation de la modèle de conception de pipeline pour les serveurs Web en utilisant rack .

très proprement sépare les différentes étapes du traitement d'une demande -. Séparation des préoccupations étant un objectif clé de tous les produits logiciels bien conçus

Par exemple avec rack je peux avoir des étapes séparées du pipeline faisant:

  • Authentification : lorsque la demande arrive, sont les utilisateurs se connectent correctement les détails? Comment puis-je valider cette OAuth, authentification HTTP de base, nom / mot de passe?

  • Autorisation :. "? Est l'utilisateur autorisé à effectuer cette tâche particulière", à savoir la sécurité basée sur les rôles

  • Mise en cache : ai-je cette demande déjà traitée, puis-je retourner un résultat en cache

  • Décoration : comment puis-je améliorer la demande de faire le traitement en aval mieux

  • Performances et Surveillance de l'utilisation : ce que les statistiques puis-je obtenir de la demande et la réponse

  • Exécution :. En fait manipuler la demande et de fournir une réponse

Être capable de séparer les différentes étapes (et éventuellement les inclure) est une grande aide dans le développement d'applications bien structurées.

Communauté

Il y a aussi un grand éco-système de développement pour rack autour Middleware - vous devriez être en mesure de trouver des composants rack pré-construit pour faire toutes les étapes ci-dessus et plus. Voir le wiki GitHub Rack pour une liste de middleware .

Quoi de Middleware?

Middleware est un terme terrible qui fait référence à tout composant logiciel / bibliothèque qui aide avec mais n'est pas directement impliqué dans l'exécution d'une tâche. Des exemples très communs sont l'exploitation forestière, l'authentification et les autres composants de traitement communes, horizontales . Ceux-ci ont tendance à être des choses que tout le monde a besoin à travers de multiples applications, mais pas trop de gens sont intéressés (ou devrait être) en eux-mêmes la construction.

Plus d'informations

Autres conseils

Tout d'abord, Rack est exactement deux choses:

  • Une convention d'interface serveur web
  • Un petit bijou

Rack - L'interface serveur web

Les bases du rack est une simple convention. Chaque support serveur web compatible toujours appeler une méthode d'appel sur un objet que vous lui donnez et servir le résultat de cette méthode. Rack spécifie exactement comment cette méthode d'appel doit ressembler, et ce qu'il doit revenir. C'est rack.

Donnons un essai simple. Je vais utiliser WEBrick comme support serveur web compatible, mais aucun d'entre eux faire. Créons une simple application web qui renvoie une chaîne JSON. Pour cela, nous allons créer un fichier appelé config.ru. Le config.ru sera automatiquement appelée par la commande de bijou rack de rackup qui simplement exécuter le contenu du config.ru dans un serveur web compatible rack. Donc, nous allons ajouter ce qui suit au fichier config.ru:

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

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

Comme la convention spécifie notre serveur a un appel de méthode appelé qui accepte un hachage de l'environnement et retourne un tableau avec la forme [état, en-têtes, corps] pour le serveur Web pour servir. Essayons simplement par appeler rackup. Un serveur compatible rack par défaut, WEBrick ou peut-être Mongrel commencera immédiatement et attendre les demandes de servir.

$ 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

Testons notre nouveau serveur JSON soit le curling ou en visitant le http://localhost:9292/hello.json url et le tour est joué:

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

Il fonctionne. Génial! C'est la base pour chaque framework web, que ce soit Rails ou Sinatra. À un certain moment, ils mettent en œuvre une méthode d'appel, le travail à travers tout le code-cadre, et enfin revenir une réponse typique [état, en-têtes, corps] forme.

Dans Ruby on Rails par exemple les demandes rack frappe la classe ActionDispatch::Routing.Mapper qui ressemble à ceci:

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 essentiellement des contrôles, qui dépendent de la valeur de hachage env si les correspondances de route. Dans ce cas, il passe le hachage env à l'application pour calculer la réponse, sinon il répond immédiatement avec un 404. Ainsi, tout serveur Web qui est conforme à la convention d'interface de rack, est en mesure de servir une application Rails entièrement soufflé.

Middleware

rack prend également en charge la création de couches de middleware. Ils interceptent essentiellement une demande, faire quelque chose et de le transmettre. Ceci est très utile pour les tâches polyvalentes.

Disons que nous voulons ajouter la journalisation à notre serveur JSON qui mesure aussi combien de temps une demande prend. Nous pouvons simplement créer un enregistreur de middleware qui fait exactement ceci:

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

Quand il est créé, il se sauve une copie de la demande de support réelle. Dans notre cas, c'est une instance de notre JSONServer. Rack appelle automatiquement la méthode d'appel sur le middleware et attend de nouveau un tableau de [status, headers, body], tout comme nos retours JSONServer.

Donc, dans ce middleware, le point de départ est prise, l'appel réel à la JSONServer est faite avec @app.call(env), l'enregistreur émet l'entrée de l'exploitation forestière et, enfin, renvoie la réponse que [@status, @headers, @body].

Pour notre petit rackup.ru utiliser ce middleware, ajoutez une utilisation RackLogger à elle comme ceci:

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   

Redémarrez le serveur et le tour est joué, il émet un journal à chaque demande. Rack vous permet d'ajouter plusieurs intergiciels qui sont appelés dans l'ordre dans lequel ils sont ajoutés. Il est juste un excellent moyen d'ajouter des fonctionnalités sans changer le noyau de l'application du rack.

Rack - Le Gem

Bien que porte - avant tout - est une convention, il est également un petit bijou qui offre une grande fonctionnalité. L'un d'eux que nous avons utilisé pour notre serveur JSON, la commande rackup. Mais il y a plus! Le bijou rack fournit peu d'applications pour un bon nombre de cas d'utilisation, comme des fichiers statiques au service ou même des répertoires entiers. Voyons comment nous servons un simple fichier, par exemple un fichier HTML très basique situé à htmls / index.html:

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

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

Nous voulons peut-être servir ce fichierà partir de la racine de notre site Web, nous allons ajouter ce qui suit à notre config.ru:

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

Si nous visitons http://localhost:9292 nous voyons notre fichier html parfaitement rendu. C'est était facile, pas vrai?

Ajoutons un répertoire complet de fichiers javascript en créant des fichiers javascript sous / javascripts et en ajoutant ce qui suit à la config.ru:

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

Redémarrez le serveur et visite http://localhost:9292/javascript et vous verrez une liste de tous les fichiers javascript vous pouvez inclure maintenant de droit partout.

J'ai eu un problème à comprendre moi-même rack pour une bonne quantité de temps. Je ne l'ai bien compris, après avoir travaillé à faire de cette miniature serveur web Ruby moi-même. J'ai partagé mes leçons apprises au sujet Rack (sous la forme d'une histoire) ici sur mon blog:

middleware Rack est un moyen de filtrer une demande et la réponse à venir dans votre application. Un composant middleware se trouve entre le client et le serveur, le traitement des demandes entrantes et sortantes réponses, mais il est plus que l'interface qui peut être utilisé pour parler au serveur Web. Il est utilisé pour les modules de groupe et l'ordre, qui sont généralement les classes Ruby, et spécifier la dépendance entre eux. module middleware Rack doit seulement: - avoir constructeur qui prend ensuite l'application dans la pile en tant que paramètre - répondre à la méthode « appel », qui prend hachage de l'environnement en tant que paramètre. De retour la valeur de cet appel est un tableau de:. Code d'état, hachage de l'environnement et le corps de réponse

Je l'ai utilisé middleware Rack pour résoudre quelques problèmes:

  1. Attraper erreurs d'analyse JSON avec la coutume middleware Rack et le retour des messages d'erreur bien formatés lorsque le client soumet Busted JSON
  2. Compression de contenu via rack :: Deflater

Il a donné dans les deux cas fixe assez élégant.

Qu'est-ce rack?

rack fournit une interface entre un minimum entre les serveurs Web supportant les cadres Ruby et Ruby.

Utilisation de rack, vous pouvez écrire une application Rack.

rack passera le hachage environnement (un Hash, contenu dans une requête HTTP d'un client, composé des en-têtes de CGI-like) à votre application Rack qui peut utiliser des choses contenues dans ce hachage pour faire ce qu'il veut.

Qu'est-ce qu'une application Rack?

Pour utiliser Rack, vous devez fournir une « application » - un objet qui répond à la méthode #call avec le Hash environnement en tant que paramètre (généralement défini comme env). #call doit retourner un tableau d'exactement trois valeurs:

  • Code de statut (par exemple, '200'),
  • Hash des en-têtes ,
  • Corps de la réponse (qui doit répondre à la méthode Ruby, each).

Vous pouvez écrire une application Rack qui retourne un tel tableau - ce sera renvoyé à votre client, par rack, dans un Réponse (ce sera effectivement un exemple de la classe Rack::Response [cliquez pour aller docs]).

Une application Rack très simple:

  • gem install rack
  • Créez un fichier config.ru - Rack sait chercher ce
  • .

Nous allons créer une petite application de rack qui retourne une réponse (une instance de Rack::Response) qui est l'organe de réponse est un tableau qui contient une chaîne:. "Hello, World!"

Nous tirerons un serveur local en utilisant la commande rackup.

Lors de la visite du port concerné dans notre navigateur, nous verrons « Bonjour, monde! » rendu dans la fenêtre.

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

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

run MessageApp.new

Incendie d'un serveur local avec rackup et visitez localhost: '! Bonjour, monde' 9292 de et vous devriez voir rendu.

Ce n'est pas une explication complète, mais essentiellement ce qui se passe ici est que le client (le navigateur) envoie une requête HTTP au rack, via votre serveur local et rack instancie MessageApp et court call, en passant dans le Hash Environnement comme paramètre dans la méthode (l'argument de env).

rack prend la valeur de retour (le tableau) et l'utilise pour créer une instance de Rack::Response et l'envoie au client. Le navigateur utilise magie pour imprimer "Bonjour, monde! à l'écran.

Par ailleurs, si vous voulez voir ce que le hachage de l'environnement ressemble, il suffit de mettre puts env sous def call(env).

Minimal comme il est, ce que vous avez écrit ici est une application Rack!

Faire interagir application Rack avec le hachage Environnement entrant

Dans notre petite application Rack, nous pouvons interagir avec le hachage env (voir ici pour en savoir plus sur le hachage de l'environnement).

Nous allons mettre en œuvre la possibilité pour l'utilisateur d'entrer leur propre chaîne de requête dans l'URL, par conséquent, cette chaîne sera présent dans la requête HTTP, encapsulé comme valeur dans l'une des paires clé / valeur du hachage environnement.

Notre application Rack accédera à cette chaîne de requête du hachage environnement et nous renverrons au client (notre navigateur, dans ce cas) par l'intermédiaire du corps dans la réponse.

A partir de la documentation Rack sur le Hash Environnement: « QUERY_STRING:.! La partie de l'URL de la requête qui suit le cas échéant peut être vide, mais il est toujours nécessaire »

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

Maintenant, rackup et visiter localhost:9292?hello (?hello étant la chaîne de requête) et vous devriez voir « bonjour » rendu dans la viewport.

Middleware rack

Nous allons:

  • insérer un morceau de Middleware Rack dans notre base de code - une classe: MessageSetter,
  • le hachage environnement frappera cette classe première et sera adoptée en tant que paramètre: env,
  • MessageSetter va insérer une clé dans le hachage 'MESSAGE' env, sa valeur étant 'Hello, World!' si env['QUERY_STRING'] est vide; env['QUERY_STRING'] sinon,
  • enfin, il retournera @app.call(env) - @app être la prochaine application de la 'pile': MessageApp
  • .

D'abord, la version 'longue main':

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

Des docs rack :: Builder nous voyons que Rack::Builder met en œuvre une petite DSL pour construire itérativement applications en rack. Cela signifie que vous pouvez construire une « pile » composé d'un ou plusieurs intergiciels et une application « de niveau inférieur » à être envoyé à. Toutes les demandes en cours à votre application au niveau du bas seront d'abord traités par votre Middleware (s).

#use spécifie middleware à utiliser dans une pile. Il prend le middleware comme argument.

Middleware Rack doit:

  • un constructeur qui prend l'application suivante dans la pile en tant que paramètre.
  • répondre à la méthode de call qui prend le hachage de l'environnement en tant que paramètre.

Dans notre cas, le 'Middleware' est MessageSetter, le 'constructeur' est la méthode initialize de MessageSetter, la 'prochaine application' dans la pile est MessageApp.

Voici donc, à cause de ce Rack::Builder fait sous le capot, l'argument app de la méthode de MessageSetter de initialize est MessageApp.

(obtenir votre tête autour ci-dessus avant de passer)

Par conséquent, chaque morceau de Middleware essentiellement «passe vers le bas du hachage environnement existant à la prochaine application de la chaîne - donc vous avez la possibilité de muter ce hachage de l'environnement dans le Middleware avant de passer à la prochaine application dans la pile .

#run prend un argument qui est un objet qui répond à #call et renvoie une réponse en rack (une instance de Rack::Response).

Conclusions

Utilisation Rack::Builder vous pouvez construire des chaînes de intergiciels et toute demande de votre demande sera traitée par chaque Middleware à son tour avant d'être finalement traitées par la dernière pièce de la pile (dans notre cas, MessageApp). Cela est extrêmement utile, car elle sépare les différentes étapes de sortie de traitement des demandes. En termes de « séparation des préoccupations », il ne pouvait pas être beaucoup plus propre!

Vous pouvez construire un « pipeline de demande » composé de plusieurs intergiciels qui traitent des choses telles que:

  • Authentification
  • Autorisation
  • Mise en cache
  • Décoration
  • Performances et Surveillance de l'utilisation
  • Exécution (poignée en fait la demande et de fournir une réponse)

(au-dessus des points de balle d'une autre réponse sur ce fil)

Vous verrez souvent cela dans les applications professionnelles Sinatra. Sinatra utilise rack! Voir pour la définition de Sinatra IS

Comme note finale, notre config.ru peut être écrit dans un style sténographique, produisant exactement les mêmes fonctionnalités (et c'est ce que vous verrez généralement):

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

use MessageSetter
run MessageApp.new

Et pour montrer de manière plus explicite ce que MessageApp est en train de faire, voici sa version « longue main » qui montre explicitement que #call crée une nouvelle instance de Rack::Response, avec les trois arguments requis.

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

Liens utiles

Rack - L'interface b / w Web et serveur d'applications

Rack est un paquet Ruby qui fournit une interface pour un serveur Web pour communiquer avec l'application. Il est facile d'ajouter des composants middleware entre le serveur Web et l'application de modifier la façon dont votre demande / réponse se comporte. Le composant intermédiaire se trouve entre le client et le serveur, le traitement des demandes entrantes et les réponses sortantes.

En termes simples, il est fondamentalement juste un ensemble de lignes directrices pour la façon dont un serveur et une application Rails (ou toute autre application Web Ruby) doivent communiquer entre eux .

Pour utiliser Rack, fournir une « application »: un objet qui répond à la méthode d'appel, en prenant la valeur de hachage de l'environnement en tant que paramètre, et le retour d'un tableau comportant trois éléments:

  • Le code de réponse HTTP
  • Hash des en-têtes
  • corps de réponse , qui doit répondre à chaque demande .

Pour plus d'explications, vous pouvez suivre les liens ci-dessous.

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

Dans les rails, nous avons config.ru en tant que fichier rack, vous pouvez exécuter un fichier rack avec commande rackup. Et le port par défaut pour cela est 9292. Pour tester cela, vous pouvez simplement lancer rackup dans votre répertoire rails et voir le résultat. Vous pouvez également affecter le port sur lequel vous voulez exécuter. Commande pour exécuter le fichier de rack sur un port spécifique est

rackup -p PORT_NUMBER
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top