28

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?

Phrogz
  • 296,393
  • 112
  • 651
  • 745
punund
  • 4,321
  • 3
  • 34
  • 45
  • I give two solutions for you below. However, note this quote from the [linked discussion](https://github.com/sinatra/sinatra/issues/253#issuecomment-1102620): _"I'm unaware of when it is correct for a GET or POST to the same path should result in identical behavior/responses. I've always found it to be the case that there is a minor difference in behavior, although the response may be the same. This is what templates/views are for. Any functionality they may share can easily be wrapped in a another method."_ Are you sure that you really want the exact same route for both `get` and `post`? – Phrogz Dec 07 '11 at 23:42
  • Thank you. I would like still to be able to differentiate between the verbs inside the route. It doesn't seem that multi_route supports this, but it must be trivial to add. – punund Dec 08 '11 at 09:32
  • I've updated my answer to show that you can always use `request.env["REQUEST_METHOD"]` to determine what the verb is inside your route. The fact that you care makes it even more evident that you probably should have two separate routes with one or more shared helpers and views; however, the beauty of Sinatra is that there is no one right way...so enjoy! :) – Phrogz Dec 08 '11 at 16:00

4 Answers4

34

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.

Phrogz
  • 296,393
  • 112
  • 651
  • 745
10

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.

Peter Ehrlich
  • 6,969
  • 4
  • 49
  • 65
Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • 1
    This is the perfect solution by far. You remind me again that it's all Ruby and blocks :) – Janko Mar 03 '15 at 02:38
5

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
Adam Bowen
  • 656
  • 8
  • 8
0

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.

TKH
  • 828
  • 6
  • 11