Dans mon introduction à rack je vous ai laissé sur votre faim concernant la création de middleware.
Nous allons donc voir comment faire un mini-logger.
Tout d’abord ne pas oublier de rajouter le dossier lib dans l’autoload:
module App
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/lib)
end
end
Ensuite on crée une class Tracking dans notre dossier lib:
class Tracking
def initialize(app)
@app = app
end
def call(env)
request = Rack::Request.new(env)
File.open(File.join(Rails.root, "log", "custom.log"), "a") do |f|
f << "IP: #{request.ip}, PATH: #{request.path}, PARAMS: #{request.params}\n"
end
@app.call(env)
end
end
Et voilà notre middleware d’une 10aine de ligne.
Quelques explications:
Par exemple je pourrai définir env[‘foo’] = ‘bar’ et y avoir accès dans le middleware suivant.
L’invocation de app.call(env) va renvoyer le status de la requête (code http, ex: 200), les entêtes et la réponse. Le middleware peut donc modifier ces 3 éléments.
Ici nous nous contentons de parser la requête à partir de l’environnement entrant à l’aide de rack. Ensuite nous avons accès à toutes les méthodes classiques de request.
Dans notre fichier d’environnement, par exemple developement.rb il suffit de définir les middlewares de la sorte:
App::Application.configure do
config.middleware.delete("Rails::Rack::Logger")
config.middleware.insert_after("ActionDispatch::ParamsParser", "Tracking")
# config.middleware.use("Tracking")
end
Il est important de bien utiliser les versions quotées des classes. L’utilisation de la constante entrainerait une exception, car la classe ne serait pas encore chargée.
On a ici commencé par retirer le logger de rack pour ajouter notre middleware après le parser. La dernière ligne montre l’utilisation de use, qui permet d’ajouter le middleware en fin de liste.
On remarque que Rails 3 est vraiment modulable, l’ajout ou la suppression d’un middleware est très simple, rendant le framework très souple.
Bon, notre middleware est vraiment basique, mais vous allez voir qu’un middleware plus pratique tel qu’Etag ne fait pas grand chose de plus compliqué.
require 'digest/md5'
module Rack
# Automatically sets the ETag header on all String bodies
class ETag
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
if !headers.has_key?('ETag')
digest, body = digest_body(body)
headers['ETag'] = %("#{digest}")
end
[status, headers, body]
end
private
def digest_body(body)
digest = Digest::MD5.new
parts = []
body.each do |part|
digest << part
parts << part
end
[digest.hexdigest, parts]
end
end
end
L’initialisation est identique. Le call va récupérer le status, les entêtes et la requête.
Ensuite le contenu de la requête est hashé et un entête Etag avec le checksum MD5 ajouté, ce qui permettra au serveur de servir directement le contenu s’il a déjà été calculé.
Et c’est tout !
L’équipe Synbioz.
Libres d’être ensemble.