#151
Mar 02, 2009

Rack Middleware

Rack middleware is a way to filter a request and response coming into your application. In this episode I show how to modify the response body using middleware.
Download (25.2 MB, 12:42)
alternative download for iPod & Apple TV (15.9 MB, 12:42)

Resources

Special thanks to Josh Peek for answering some questions I had on this topic.

Update: making the code below thread safe by duplicating self on call.

Note: as wldock and remi pointed out in the comments, it is important to change the headers["Content-Length"] value to reflect the new body length. See their comments for details.

rake middleware
# lib/response_timer.rb
class ResponseTimer
  def initialize(app, message = "Response Time")
    @app = app
    @message = message
  end
  
  def call(env)
    dup._call(env)
  end
  
  def _call(env)
    @start = Time.now
    @status, @headers, @response = @app.call(env)
    @stop = Time.now
    [@status, @headers, self]
  end
  
  def each(&block)
    block.call("<!-- #{@message}: #{@stop - @start} -->\n") if @headers["Content-Type"].include? "text/html"
    @response.each(&block)
  end
end

# config/environment.rb
config.middleware.use "ResponseTimer", "Load Time"

RSS Feed for Episode Comments 85 comments

1. Dougal MacPherson Mar 02, 2009 at 01:00

I'm really enjoying your Rails 2.3 screencasts.
Thanks again.


2. Dave Hollingworth Mar 02, 2009 at 01:01

Hi Ryan,

another great screencast as usual - thanks! A quick question - you mention that it wouldn't be a good idea to use your example in a production environment - why not? I'd liker to display the response time on a page (like Google does on a search results page for example), and this looks like a good way to do it. Is there a performance hit by using this perhaps?


3. Deger Mar 02, 2009 at 01:15

Interesting screencast as usual, thank you!


4. RailsCasts Fan Mar 02, 2009 at 01:38

Ah, the #151 is here ! Very good Ryan, as always ! ;-) I hope a #1000 and even a #2000. ^^


5. Graham Mar 02, 2009 at 05:13

Sounds useful, but I have to admit you lost me on this one Ryan. Lots of unfamiliar territory.


6. Joshua Peek Mar 02, 2009 at 07:08

Awesome!

Maybe not a huge concern for your example, but it is not threadsafe. You should never modify instance variables in the call method.

However, there is a quick trick to get around this: http://pastie.org/404695


7. remi Mar 02, 2009 at 08:14

If anyone wants a more in-depth look at how to write Rack Middleware (and Rack apps), you might like my Rack Middleware screencast:

http://remi.org/2009/02/28/rack-part-3-middleware.html

Thanks for showing off all of the sweet things that we get with Rails 2.3, now that Rails has such awesome Rack support :)


8. Stephen Celis Mar 02, 2009 at 08:20

A really nice overview, but with a potential gotcha for those that that use the example in a real app:

Nothing should ever appear before the DOCTYPE.

You'll start seeing some definite quirks in different browsers if anything not-DOCTYPE comes first, including comments.


9. Shreyans Mar 02, 2009 at 08:21

Nice episode. Keep it going.
Ryan, can you give me some more specific areas where it can be used in production environment? I mean more scenarios where it makes sense to use RACK middleware!


10. wlodek Mar 02, 2009 at 08:29

I think, that @headers['Content-Length'] should by updated to reflect the change in the response length. Otherwise Rack::Lint will complain.


11. Fredrik Mar 02, 2009 at 08:46

Middleware seems quite powerfull. I am currently using Sven Fuchs routing-filter plugin so I can support more search friendly urls. I am using it so my cms can respond to resource path's stored in the database (like: host/research/forums). Maybe it could be solved with a middleware instead?


12. remi Mar 02, 2009 at 11:08

@wlodek yep, if you do this for real, you absolutely need to update the Content-Length, and not just for Rack::Lint.

If you add text to the response body without updating the Content-Length, the HTML you see in the browser may be truncated (because the browser is only displaying the *original* number of characters, before you added more text to the response body).

Ofcourse, you can use another middleware to ensure that Content-Length is always set to the length of the response body. Rack comes out of the box with Rack::ContentLength for doing something like this, but I think it only works under certain conditions. It would be easy to write your own middleware to accomplish this, if need be, but it's a good idea for your middleware to always make sure to update the Content-Length, itself.


13. David Knorr Mar 02, 2009 at 11:58

@Dave Hollingworth: I'm pretty sure Ryan ment that adding a _comment_ before the DTD would not be really useful in production environment. If you want the user to see the response time it might be useful.

Cheers,
David Knorr.


14. Roman Le Négrate Mar 02, 2009 at 14:16

@Ryan: Nice screencast, as usual.
@Joshua Peek: +1, except I would rename '_call' to the more conventional 'call!'.


15. Shreyans Mar 02, 2009 at 14:35

Hi Ryan, thanks for this nice episode. One thing I need to request you. It would be too nice of you if you could dedicate one episode on GSA (Google Search Appliance) search. I know many people are using it and it would really help people learn it further if you produce one episode on how to implement GSA search in Rails App.


16. Rob Anderton Mar 03, 2009 at 01:00

@Shreyans:

I blogged a little while back about how I used middleware to help with a flash file uploader:

http://tinyurl.com/desshu

While the middleware doesn't look like it does much, it replaces a much more hacky and lengthy solution that had to be used in the 'olden days' :D


17. Ryan Bates Mar 03, 2009 at 08:40

@Dave, I would not use this specific middleware in production because it is not the best way to measure performance. Instead you should use something like NewRelic's RPM. It is also bad to add an HTML comment to the very top of the page before the doctype.

I think Google's response time display is primarily the time the search took to process, I would do that within the Rails app itself and not though middleware.

@Graham, sorry to lose you. This is a fairly advanced topic. Expect some more intermediate content soon.

@Joshua, thanks for pointing this out! I'll update the code so it is thread safe.

@Stephen, right, placing a comment above the doctype is not ideal. This is one reason why I mentioned to not do this in production.

@Shreyans, check out the rack-contrib project linked to in the show notes. That has a lot of production ready middleware examples.

@wlodek, @remi good point! I'll add a note about this.


18. Amit Levy Mar 03, 2009 at 12:55

Ryan,
I've been a big fan for some time now, however one comment:

It seems to me that your essentially using instance variables instead of parameter passing. With the addition to what @Joshua pointed out, this makes this code very hard to test. This example is maybe too simple to illustrate this, but more complex certainly would need some unit tests.
This structure means you would have to either invoke call before invoking each in your tests, or use Object#instance_variable_get, which is ugly at best.
Perhaps a better way might be to create a business object that wrapped response with all the other variables you need:
http://pastie.org/406393

This is both thread safe *and* testable


19. Ryan Bates Mar 03, 2009 at 15:34

@Amit, good suggestion. That does make things easier to test and elegantly handles the thread-safety issue. One might also consider nesting the body class within the timer. I feel better about keeping them in the same file this way.
http://pastie.org/406597


20. Jaime Iniesta Mar 05, 2009 at 04:24

Thanks Ryan, a very interesting railscast, and the link to rack-contrib helps newbies like me understand what all this rack middleware stuff is good for. :)


21. David Beckwith Mar 06, 2009 at 03:28

Hello Ryan,

  Great screencast as usual. Can you explain a little about

block.call( "... stuff .... ")
@response.each(&block)

  How is it that "...stuff..." gets inserted into the response.body by calling the block? And how is that equivalent to "...stuff...." + response.body?

Thank you,
David :)


22. Raj Mar 06, 2009 at 23:26

Ryan,

I've been watching your railscasts and liked them very much.

In one of them you are watching the contents of development.log on OS X terminal as it changes.

Please let me know the command/tool to watch the runtime changes to the files.

Thanks in advance.

Regards,
Rajan


23. remi Mar 06, 2009 at 23:40

@DavidBeckwith: the answers to your questions can be found in the Rack interface specification:

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

Note that the body of a Rack app (and middleware are Rack apps) is anything that responds to #each and returns String values. Ryan could have built the body of the response and then prepended it with "..." but, instead, he basically says "the first time #each is called, i'll return my custom string, then i'll just pass the block along to the response's #each".

For more, definitely check out the Rack interface specification or my Rack screencasts @ http://remi.org


24. Melvin Ram - BrainBank Mar 08, 2009 at 17:00

Thanks Ryan! Here's my notes for this screencast (also available at:
http://railsnotes.wordpress.com/2009/03/08/rails-rack-middleware-screencast-notes )

RailsCasts #151 Rack Middleware Notes

* Rack Middleware = filter which you can use to intercept a request and handle the behavior a little differently as it goes to and from the application

* Different between middleware & metal = Metal is an endpoint & middleware is designed to be more of a filter (changes behavior if it needs to.)

* Goes into /lib directory as a .rb file?

* All middleware takes a initialize(app) method. The app variable will hold the rails application

* Adding a call(env) method will make it override rails. This needs to return a array with the same 3 elements as a metal.

* to make rails use your middleware, add this to your environment.rb file inside the Initializer block: config.middleware.use "NameOfClass"

* 'rake middleware' will list out the middlewares used by your app

* You'll need to restart the server every time you create/change a middleware

* To execute your rails app from your middleware, just run .call on the rails app, ex: @app.call(env)

* you can assign parts of the response from a rails app to variables using something like this:
  status, headers, response = @app.call(env)
  
* and then you can call return those back to the real response using something like this:
  [status, headers, "Add stuff" + response.body]
  
* response.body assumes that the app is a rails app. this is not best practice.

* a better approach is to use [status, headers, self] together with def each(&block) `


25. Melvin Ram - BrainBank Mar 08, 2009 at 17:02

Rajan,

You can watch live changes to your development.log file by doing "tail -f log/development.log"


26. Todd Tyree Mar 28, 2009 at 04:09

Ryan, thanks for that as always.

I've Been putting off moving over to 2.3 for a while now, but this convinced me that rack is the perfect solution for a problem I've been seeing with cookieless sessions whereby some phones won't send get and post parameters as part of the same request.


27. Dick Davies Apr 22, 2009 at 15:45

Great screencast as usual, thanks Ryan.

It's worth re-iterating that whenever you write middleware, it's always worth sticking Rack::Lint *on either side of it*, to check you don't inadvertantly break requests and responses.

Essentially, Rack::Lint *is* the Rack spec, so it saves you a lot of reading :)


28. Shih-gian Lee May 05, 2009 at 16:53

It is nice screencast for Rails 2.3. What about Rails 2.1? How do I use Rack in Rails 2.1? Any screencast for that?


29. Lazer May 05, 2009 at 22:25

 Hi Ryan,
Great screencast for rails 2.3.Take care of commenter Melvin Ram above,he also explains extras.

Please let me know the command/tool to watch the runtime changes to the file.


30. Gabe Nov 04, 2009 at 13:59

could this be used to create an upload status bar?


31. Harry Dec 27, 2009 at 00:57

Very good, thanks!


32. shenxian's blog Jan 11, 2010 at 22:19

Good post.It is very usefu.I like to see it because I am learn it now.Thank you.


33. Mark D Jan 20, 2010 at 01:48

Hey, just thought i'd say i had a problem running the 'hello world' bit, i was just getting a '#' on the screen. Sorted it by putting the 'Hello world' in square brackets.

def call(env)
  [200, {"Content-Type" => "text/html"}, ["Hello world"]]
end


34. Plagiarism Checker Jan 24, 2010 at 04:03

What a post, thanks for your code


35. internet branding Feb 06, 2010 at 12:06

Great article, thanks for sharing it with us


36. seo copywriting service Feb 13, 2010 at 11:24

great work, thank you


37. gas central heating boilers Feb 19, 2010 at 11:56

thanks for this nice post


38. mbtshoes Feb 20, 2010 at 03:49

Just wanted to thank you for the download.


39. Magento Mar 02, 2010 at 22:26

Excellent post, thanks for sharing with us these lines


40. The Sovereign Mar 16, 2010 at 23:12

great post you are sharing here, thanks for your effort


41. Executive MBA Mar 30, 2010 at 22:33

thanks for sharing this excellent post


42. building inspection services Apr 09, 2010 at 05:30

thanks for this nice post


43. solar street lights Apr 15, 2010 at 06:53

Great post, Thank you.


44. chinese boy Apr 21, 2010 at 02:03

This is a good post, I stumbled across your article while looking for song downloads. Thanks for sharing, I’ll be sure to recommend this site to others.


45. Liverpool Builders May 06, 2010 at 08:25

thanks for this nice code & template


46. Streamlight E-Flood Firebox May 21, 2010 at 02:46

I had received the first edition of “Make” magazine as a gift and it was really awesome…


47. First Aid Kit Refils May 24, 2010 at 00:27

This is really fabulous!
I already have some of them but I am still going to save all of them.
Thanks for your wonderful sharing.


48. receipt books Jun 08, 2010 at 09:22

very nice code lines you have here, thanks


49. Galco Jun 10, 2010 at 02:42

wow nice comments pages....


50. 5.11 tactical Jun 11, 2010 at 02:21

Great resource. Thanks for sharing.

Your site is amazing so I have featured it in my design blog..


51. Tammy taylor Jun 11, 2010 at 03:56

Great work done there. Thanks for sharing your story.


52. financial adviser Jun 16, 2010 at 00:15

Thanks For posting,Very Nice Keep up date a more article.


53. hp laptop battery Jun 16, 2010 at 19:36

hp laptop battery, maybe you will interesting.


54. Streamlight Stinger Jun 17, 2010 at 04:33

commission must impose fines or other sanctions against those who may have violated rules.


55. PutraHosting.com Dapur Hosting Hemat Jun 17, 2010 at 05:06

very helpfull post, thks


56. vector graphics Jun 20, 2010 at 03:15

I am looking to Skype for the first time with other HS students who are learning Spanish. Please
let me know if you are interested!


57. free vector graphics Jun 20, 2010 at 03:30

I am looking to Skype for the first time with other HS students who are learning Spanish. Please

let me know if you are interested!


58. Titanium Belly Ring Jun 22, 2010 at 23:25

Congratulations for that winning shot!


59. North Carolina Nursing Degree Jun 24, 2010 at 01:17

It is always important to split up the site as much as possible so that certain pages can focus on certain services. The user experience is much better as well.


60. permatec Jun 24, 2010 at 05:33

Really Nice post.Keep it up :)


61. discount nfl jerseys Jun 25, 2010 at 01:18

very interesting point.thanks for sharing.


62. Gary Jun 27, 2010 at 19:17

Three blonde women were stranded on an island. While trying to dig their way out, one of them came across a buried lamp. Suddenly a genie appears and offers to grant each one of them one wish, in return for saving him.


63. Blogger Indonesia Dukung Internet Aman, Sehat & Manfaat Jun 28, 2010 at 00:13

These kind of post are always inspiring and I prefer to read quality content so I happy to find many good point here in the post, writing is simply great, thank you for the post


64. prom dresses Jul 02, 2010 at 01:24

I think my brain just exploded when reading this post. Awesome work.


65. cat scratch fever symptoms Jul 03, 2010 at 11:22

Great post you have here, please keep up this great work


66. Bet odd casino Jul 06, 2010 at 02:49

Great work done there. Thanks for sharing your story.


67. biber hapi Jul 08, 2010 at 03:57

Interesting,Keep up the good work,
Thanks for writing, most people don't bother.


68. Sterling silver belly rings Jul 09, 2010 at 01:27

Very good blog.Thank you for sharing.Best wishes !


69. Arenabetting.com Dukung Fair Play Fifa World Cup AFSEL 2010 Jul 10, 2010 at 08:01

nice consept wiht good stuff, i has bookmark it


70. vikings jerseys Jul 11, 2010 at 22:44

nic ejerseys


71. nanida Jul 13, 2010 at 02:54

With the help of the online stores,we can buy the cheap prom dresses.If you are lucky,you can buy the <a href="http://www.promdressesin.com/dress-between-50-100-37">prom dresses under 100 dollars</a>.Do not hesitate,hurry up to have a look!


72. free cna training Jul 14, 2010 at 00:31

Considerably, this post is really the sweetest on this notable topic. I harmonise with your conclusions and will thirstily look forward to your incoming updates. Saying

thanks will not just be sufficient, for the phenomenal clarity in your writing.


73. vikings jerseys Jul 16, 2010 at 02:32

vikings jerseys www.nflvikingsjerseys.com


74. Sally Jul 16, 2010 at 07:02

Great article, I shall add it to my favourites!


75. Graham Ashton Jul 17, 2010 at 14:39

Cracking screencast, thanks. I just used metal to load Nesta (a Sinatra CMS) into the same process as a Rails 2.3 app.

I'm sure I wouldn't have found out how to do it so smoothly if I hadn't watched this.


76. iphone case Jul 21, 2010 at 23:50

Bright idea, hope there can be more useful articles about。


77. ghd hair straighteners Jul 22, 2010 at 02:06

Great!This article is creative,there are a lot of new idea,it gives me inspiration.I think I will also inspired by you and think about more new ideas


78. Cheap Gucci sale Jul 27, 2010 at 00:51

Gucci world welcomes you very much.If you are in search of the famous gucci sneakers,please come to us to have more choices.Top quality with cheap price.


79. Testking 640-553 Jul 28, 2010 at 00:56

thanks for sharing this useful information with us..


80. best seo services company Jul 29, 2010 at 10:43

thanks for tha informations


81. Heritage Hotel in Jaipur Jul 29, 2010 at 13:10

that's really a fantastic post ! added to my favourite blogs list.. I have been reading your blog last couple of weeks and enjoy every bit. Thanks.


82. fm Stereo transmitter Jul 29, 2010 at 23:12

I was just doing some web browsing on my Garmin Phone during my break at my work place, and I came across something I thought was interesting . It linked over to your website so I clicked over. I can’t really find the relevance between your site and the one I came from, but your site good none the less.thanks for sharing thoes informations , It is interesting, i like it!


83. Straightening Machine Jul 30, 2010 at 09:47

I recently came across your blog and have been reading along. I thought I would leave my first comment. I don't know what to say except that I have enjoyed reading. Nice blog. I will keep visiting this blog very often.


84. timberlandbootsuk Aug 02, 2010 at 02:05

we provide our buyers with an efficient and manageable procurement process covering every phase of the international supply chain and

streamlining trade channels. Also welcome wholesaling, feedback now!


85. lacoste polo online Aug 04, 2010 at 18:33

it is so useful to me.
thanks for your share.i will see next time,looking for your next article.
maybe you can see<a href="http://www.poloshirtsb2c.com/lacoste-polo-shirts-womens-lacoste-polo-c-2_10.html">womens lacoste polo</a>it is so useful to me.


86. logo design Aug 04, 2010 at 18:35

very nice post.middle ware are truly becoming and integral part of the infrastructure for every organization


87. cna certification Aug 05, 2010 at 05:49

Excellent news, I look forward to more of the HTML5 form stuff and especially its incorporation into JavaScript UI libraries. Except... how will web developers eat if they

can't charge for "Enter search term here" form enhancements ;-)


90. accounting logo Aug 07, 2010 at 07:46

i really like the quality of the post


90. Web Banner Aug 07, 2010 at 07:48

middle ware truly, make possible to sue application in an optimal way by taking of load from the application server and handling request


91. logo designs Aug 08, 2010 at 23:50

Thanks for the post. I enjoying your Rails 2.3 screencasts.
Thanks again.


91. swiss watch replicas Aug 10, 2010 at 14:47

High quality post you did here, thanks for your great effort to share this post with us


92. webcam videooo Aug 11, 2010 at 19:09

Very good, thanks!


93. free directory list Aug 11, 2010 at 22:33

Very useful information!


94. p90 workout Aug 12, 2010 at 09:18

I found your blog on Yahoo and I just wanted to say that I think your writing is simply stunning! Thanks again for providing this content for free.


95. cheap nfl jerseys Aug 12, 2010 at 19:37

 it is interesting and informative article. This has been very helpful understanding a lot
of things. I’m sure a lot of other people will agree with me.

 lots of good advice on your post. and as a return,i will buy a Cheap nfl jerseys to you for my thanking.


96. steroids for sale Aug 15, 2010 at 02:06

There are some very great sources here and thank you for being so kind to post them here. So we can read them and give our opinion on subject.


97. Air Jordan Spizike Aug 16, 2010 at 00:21

thanks for your share.i will see next time,looking for your next article. Thanks again for providing this content for free. lots of good advice on your post. and as a return,i will buy a Cheap nfl jerseys to you for my thanking.


98. Timberland UK Aug 16, 2010 at 23:01

quite nice ,thank you


99. wholesale new era caps Aug 20, 2010 at 20:44

These are wonderful! Thank you for finding and sharing


100. louis vuitton shoes Aug 26, 2010 at 20:56

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


101. Wholesale Electronics Aug 27, 2010 at 00:06

Discount Wholesale Electronics, Wholesale Cell Phones, Electronic Gadgets and More from the Best Dropship Wholesaler


102. logo design and logos here Aug 29, 2010 at 11:04

Thanks for sharing this - it is important to change the headers["Content-Length"] value to reflect the new body length. See their comments for details.


103. snow boots Aug 30, 2010 at 21:07

Thanks Ryan, a very interesting railscast, and the link to rack-contrib helps newbies like me understand what all this rack middleware stuff is good for. :)


104. Personalized Christmas Gifts Sep 01, 2010 at 08:12

haha,looking for your next article.


105. levis belts Sep 01, 2010 at 21:10

I feel like I’m often looking for interesting things to read about a variety of subjects, but I manage to include your blog among my reads every day because you have interesting entries that I look forward to. Here’s hoping there’s a lot more great material coming!


106. A number of low carb versions require the consumption of more fat. Others limit the fat intake or require certain amounts of various fats. Playing a definitive statement of the role of calories it really, there is currently not as the opinions are too muc Sep 01, 2010 at 21:26

A number of low carb versions require the consumption of more fat. Others limit the fat intake or require certain amounts of various fats. Playing a definitive statement of the role of calories it really, there is currently not as the opinions are too much apart.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source