Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hello World rack middleware with rails 3: how to process body of all requests

i want to try out a simple rack middleware "hello world", but i seem to get stuck. it looks like the main sytax changed, since some examples use this code:

require 'rack/utils'

class FooBar

  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
         body.body << "\nHi from #{self.class}"
         [status, headers, body]
  end
end

produces an error:

undefined method `<<' for #<ActionDispatch::Response:0x103f07c48>

even when i look at other codes out there, i cannot seem to get them running with rails 3.0.3.

here are my concrete questions:

  • how can i get a simple rack middleware to run and modify the body of any output from a rails app?
  • where should i put the Rails.application.config.middleware.use declaration? (i created an own initializer in config/initializers for that)

thanks a lot in advance!

like image 897
Hank Probes Avatar asked Jan 27 '11 23:01

Hank Probes


People also ask

What is middleware in rails?

The middleware component sits between the client and the server, processing inbound requests and outbound responses. In layman words, It is basically just a set of guidelines for how a server and a Rails app (or any other Ruby web app) should talk to each other.

What is rack middleware and why do I need It?

One useful piece of middleware is Rack::Auth::Basic, which can be used to protect any Rack app with HTTP basic authentication. It’s really lightweight and is helpful for protecting little bits of an application. For example, Ryan Bates uses it to protect a Resque server in a Rails app in this episode of Railscasts.

How do I get Hello world from rails?

To get "Hello World" from Rails, we need to create at minimum a controller and a view . A controller is needed to receive requests for the application. Routing decides which controller receives which requests.

How does rails know which web server to serve an application?

So basically Rails checks, dependent on the env hash if any route matches. If so it passes the env hash on to the application to compute the response, otherwise it immediately responds with a 404. So any webserver that is is compliant with the rack interface convention, is able to serve a fully blown Rails application.


2 Answers

This should do what you want it to:

# in config/application.rb
config.middleware.use 'FooBar'

# in config/initializers/foo_bar.rb
class FooBar
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    [status, headers, response.body << "\nHi from #{self.class}"]
  end
end

Be advised, that on just about every other request (at least on Rails 3.0.3), this will fail due to another middleware (Rack::Head) because it sends an empty request when content is unchanged. We are in this example depending on being able to call response.body, but in fact, the last member of the array can be anything that responds to .each.

Ryan Bates goes over Rack pretty well here:

http://asciicasts.com/episodes/151-rack-middleware

http://railscasts.com/episodes/151-rack-middleware

And the official Rails guide is pretty good too:

http://guides.rubyonrails.org/rails_on_rack.html

And of course the official Rack spec:

http://rack.rubyforge.org/doc/SPEC.html

like image 88
Unixmonkey Avatar answered Sep 25 '22 05:09

Unixmonkey


Rails 3.2.12+:

previous answer does not work for Rails 3.2.12+

This one does:

# in config/application.rb
config.middleware.use 'FooBar'

# in config/initializers/foo_bar.rb
class FooBar
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, response = @app.call(env)
    response.body += "\nHi from #{self.class}"
    # response.body << "..." WILL NOT WORK
    [status, headers, response]
  end
end
like image 33
zed_0xff Avatar answered Sep 23 '22 05:09

zed_0xff