How do I use a Rack middleware only for certain paths?
You could have MyMiddleware check the path and not pass control to the next piece of middle ware if it matches.
class MyMiddleware
def initialize app
@app = app
end
def call env
middlewary_stuff if env['PATH_INFO'] == '/foo'
@app.call env
end
def middlewary_stuff
#...
end
end
Or, you could use URLMap w/o the dslness. It would look something like this:
main_app = MainApp.new
Rack::URLMap.new '/'=>main_app, /^(foo|bar)/ => MyMiddleWare.new(main_app)
URLMap is actually pretty simple to grok.
How to run a simple Ruby Rack app that consists of only middleware?
A Rack app (including middleware) is basically just an object that responds to call
, accepting a hash describing the request and returning an array describing the response.
When using a rackup
file, Rack uses the DSL described in Rack::Builder
. The run
method just sets the “base” app:
def run(app)
@run = app
end
The use
method stores the middleware classes you want to include in your app, and then when constructing the final app does something like this for each one (in reverse order of how they appear in the config.ru
file):
@run = MiddleWare.new(@run, other_args)
It’s a little more complex than this, but this is the general idea. A new instance of the middleware class is created, with the existing app passed as the first argument of the constructor, and the resulting object becomes the new app. This is the (not well documented) “interface” to Rack middleware: the first argument to its initializer is the app it is wrapping, the rest are any other arguments passed to use
.
The DSL expects there will always be either a run
or map
statement, you will get an error if you omit both.
If your middleware is written in such a way that it can handle having no arguments passed to its initializer and it will behave like a full app, then you may be able to use it directly as the app in config.ru
:
run SomeMiddleware.new
This is what Sinatra does to allow it to be used as middleware. It stores the app in the initializer if it is given, and then when a request arrives that doesn’t match any route it uses the presence of @app
to decide whether to behave as middleware and pass the request on, or to behave as the final app and treat it as a not found error.
If your middleware doesn’t have this flexibility then you will need to provide an app for it to wrap, as you have in your example. In this case it may also be useful to have the app for error handling, in the event that the middleware doesn’t correctly handle a request and tries to pass it on to the wrapped app.
If you want to avoid using separate use
and run
statements in your config.ru
, you could just use run
, and pass a simple app directly to your middleware:
run SomeMiddleware.new(->(e){
[500, {'Content-type' => 'text/plain'}, ["Error: SomeMiddleware didn't handle request"]]
}
Note how this follows the interface described above for middleware: the first argument to the initializer is the app to wrap.
How to test if some specific rack middleware is being used?
With help from ruby-raven
guys, we've got this:
ENV['RACK_ENV'] = 'test'
# the app: start
require 'sinatra'
require 'sentry-raven'
Raven.configure(true) do |config|
config.dsn = '...'
end
use Raven::Rack
get '/' do
'Hello, world!'
end
# the app: end
require 'rspec'
require 'rack/test'
Raven.configure do |config|
logger = ::Logger.new(STDOUT)
logger.level = ::Logger::WARN
config.logger = logger
end
describe 'app' do
include Rack::Test::Methods
def app
@app || Sinatra::Application
end
class TestRavenError < StandardError; end
it 'sends errors to sentry' do
@app = Class.new Sinatra::Application do
get '/' do
raise TestRavenError
end
end
allow(Raven.client).to receive(:send).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven.client).to have_received(:send)
end
end
Or if raven
sending requests is in the way (when tests fail because of raven
sending requests, and not because of the underlying error), one can disable them:
Raven.configure(true) do |config|
config.should_send = Proc.new { false }
end
And mock Raven.send_or_skip
instead:
...
allow(Raven).to receive(:send_or_skip).and_return(true)
begin
get '/'
rescue TestRavenError
end
expect(Raven).to have_received(:send_or_skip)
...
Toggle Mixpanel Middleware Call in Ruby
it seems that mixpanel have updated their gem
Prevent middleware from inserting code
Note: Only applies when Rack Middleware is setup.
Occasionally you may need to send a request for HTML that you don't want the middleware to alter. In your AJAX request include the header "SKIP_MIXPANEL_MIDDLEWARE" to prevent the mixpanel code from being inserted.$.ajax("/path/to/api/endpoint", {
headers: {"Skip-Mixpanel-Middleware": true}, // valid http headers don't allow underscores and get filtered by some webservers
success: function(data) {
// Process data here
} });
//Alternatively, you can add this line of code to your controller to temporarily disable the middleware:
Mixpanel::Middleware.skip_this_request
Taken from:
https://github.com/zevarito/mixpanel#prevent-middleware-from-inserting-code
Ruby on rails: how to exclude certain path from rack middleware authentication?
If you want to exclude only "home/users/" path then you middleware should have following structure,
def call(env)
request = Rack::Request.new(env)
return @app.call(env) if request.path == "home/users/"
# your middleware logic of authentication here.
end
For more information of rack, you can refer this.
Ruby on Rails Rack middleware exclude
Damien Matheiu's answer to the question you link will absolutely work for you. You'll just need to write an appropriate filter expression.
For the example above, that would be something like:
unless env['REQUEST_PATH'].match /^\/api\/v2\/gadgets\/(\d+)\/specs$/
middleware = BasicAuth.new #... args ...
env = middleware.call(env)
end
So you will call the BasicAuth middleware only when the path doesn't match any of your excluded paths.
Related Topics
Implementing Dry-Run in Ruby Script
On Destroying Session via Devise "Couldn't Find User with 'Id'=Sign_Out"
Delayedjob: "Job Failed to Load: Uninitialized Constant Syck::Syck"
How to Properly Create a Associated Record Just After the Associator Record Creation
How to Fix My Cucumber Expectation Error When Polling
API Post with Array Using Http Gem (Or Restclient)
Iterate and Set Ruby Object Instance Variables
How to Collect Real-Time Tweets
Practical Example of Architecture Using Ebc
How to Capture Terminal Arrow Keys in Ruby
Rails - How to Add Contacts to Sendgrid Marketing Campaigns via API
Replicating Xml Request with Savon/Ruby
How to Set Up These Crud Controller Actions for Has_Many_Polymorphs and an Error
Newbie: Text Replacement in My Case