Verb-agnostic matching in Sinatra

2019-02-02 21:15发布

问题:

We can write

get '/foo' do
  ...
end

and

post '/foo' do
  ...
end

which is fine. But can I combine multiple HTTP verbs in one route?

回答1:

This is possible via the multi-route extension that is part of sinatra-contrib:

require 'sinatra'
require "sinatra/multi_route"
route :get, :post, '/foo' do
  # "GET" or "POST"
  p request.env["REQUEST_METHOD"]
end

# Or for module-style applications
class MyApp < Sinatra::Base
  register Sinatra::MultiRoute
  route :get, :post, '/foo' do
    # ...
  end
end

However, note that you can do this simply yourself without the extension via:

foo = lambda do
  # Your route here
end
get  '/foo', &foo
post '/foo', &foo

Or more elegantly as a meta-method:

def self.get_or_post(url,&block)
  get(url,&block)
  post(url,&block)
end

get_or_post '/foo' do
  # ...
end

You might also be interested in this discussion on the feature.



回答2:

FWIW, I just do it manually, with no helper methods or extensions:

%i(get post).each do |method|
   send method, '/foo' do 
       ...
   end
 end

Although if you're doing it a lot it of course makes sense to abstract that out.



回答3:

Phrogz has a great answer, but if either lambdas or including sinatra-contrib isn't for you, then this meta method will achieve the same result as sinatra-contrib for your purposes:

# Provides a way to handle multiple HTTP verbs with a single block
#
# @example
#   route :get, :post, '/something' do
#       # Handle your route here
#   end
def self.route(*methods, path, &block)
    methods.each do |method|
        method.to_sym
        self.send method, path, &block
    end
end

If you're a little wary of being able to send arbitrary methods to self, then you can protect yourself by setting up a whitelist of allowed methods in an array, and then checking for the symbol's presence in the array.

# Provides a way to handle multiple HTTP verbs with a single block
#
# @example
#   route :get, :post, '/something' do
#       # Handle your route here
#   end
def self.route(*methods, path, &block)
    allowed_methods = [:get, :post, :delete, :patch, :put, :head, :options]
    methods.each do |method|
        method.to_sym
        self.send(method, path, &block) if allowed_methods.include? method
    end
end


回答4:

Here's a service-unavailable server that I managed to get on single line :)

require 'sinatra';set port: ARGV[0]||80;%w.get post put patch options delete..map{|v|send(v,'*'){503}}

I actually used this to test the behavior of some client code in the face of 503s.



标签: ruby sinatra