Best way to verify request method

I did some googling, but didn’t find anything that specifically
addressed my question, but if you have a link, I’d really appreciate it.

I want to build some robustness into my controllers by blocking access
to actions via the wrong request method. For example, I don’t want to
allow a POST to the :index action. I started using the verify method,
such as

verify :method => :get, :only => [:index, :new, :edit], :redirect_to =>
whatever

This worked well until I added some before_filters. It appears to me
that before_filters are processed before verify, and that is opposite
what I am trying to achieve. I’d like to check the request method before
anything else happens.

I came up with an approach in which I use a real before filter and a
hash of controllers, methods, and acceptable actions. It looks like

class ApplicationController < ActionController::Base

before_filter :verify_request_method

protected

def verify_request_method
if !request_method_valid?(controller_name.to_sym,
action_name.to_sym, request.method.to_sym)
flash[:error] = ‘Invalid request method.’
redirect_to error_path and return false
end

return true

end

private

def request_method_valid?(controller, action, method)
valid_request_methods = {
:welcome => {
:get => [:index, :about],
:post => [],
:put => [],
:delete => []
},
:users => {
:get => [:show, :new, :edit],
:post => [:create],
:put => [:update],
:delete => []
},
:sessions => {
:get => [],
:post => [:new],
:put => [],
:delete => [:destroy]
}
}

return valid_request_methods[controller][method].include?(action)

end
end

This works, but I’m curious if I’m making it more difficult than it
needs to be. Is there an easier way?

Peace,
Phillip

I think you are a bit confused. You mention “It appears to me
that before_filters are processed before verify, and that is opposite
what I am trying to achieve. I’d like to check the request method before
anything else happens.”

But there is nothing magic about verify. Check out the source for
verify:

http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html

ie:

def verify(options={})
filter_opts = { :only => options[:only], :except => options[:except] }
before_filter(filter_opts) do |c|
c.send! :verify_action, options
end
end

verify IS a before filter. You just have to make sure you order it
properly so that it is the first filter to run. You really should stick
with verify since it is already a before filter you sort of just
reimplemented verify…

Nathan E. wrote:

I think you are a bit confused. You mention “It appears to me
that before_filters are processed before verify, and that is opposite
what I am trying to achieve. I’d like to check the request method before
anything else happens.”

But there is nothing magic about verify. Check out the source for
verify:

http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html

ie:

def verify(options={})
filter_opts = { :only => options[:only], :except => options[:except] }
before_filter(filter_opts) do |c|
c.send! :verify_action, options
end
end

verify IS a before filter. You just have to make sure you order it
properly so that it is the first filter to run. You really should stick
with verify since it is already a before filter you sort of just
reimplemented verify…

Thanks, Nathan. I knew that verify is essentially a before_filter,
but I guess I never thought about it being a before filter. Now that
I think of it, I may have positioned it after the before_filters in my
controller. Not sure why. And that, of course, would have produced the
before_filter first, verify last pattern that caused me problems.

As to your comment about me reinventing verify, that’s exactly what I
did because I didn’t know what else to do. I’d much stay with what’s
already built in, so I will reposition verify to the head of my
controllers and re-test. The advantage to my approach, though, is that
it’s all in one place, and if I want/need to change the action to take,
I only have to do it in one place. I don’t, however, have all of the
other capabilities that verify has.

Thanks for clearing up some of my confusion.

Peace,
Phillip

Are named routes not recognized in before_filters? I changed my
controller to

class UsersController < ApplicationController
verify :method => :get, :only => [:show, :new, :edit], :add_flash =>
{:error => ‘Invalid request method’}, :redirect_to => error_path
verify :method => :post, :only => [:create], :add_flash => {:error =>
‘Invalid request method’}, :redirect_to => error_path
verify :method => :put, :only => [:update], :add_flash => {:error =>
‘Invalid request method’}, :redirect_to => error_path

and get an error that ‘error_path’ is an undefined local variable, yet
it works in other places as one would expect. I also tried using a
helper to return error_path (which is already a helper!), and that
didn’t work either.

I’m also thinking that my method, though not “proper” Rails, is more DRY
and easier to use. In my case, I put the filter in
ApplicationController so all controllers can use it automatically. I
need to add one hash per controller for the actions I want to protect,
and that’s it. I need to put an exclude test in there for any
controller that I don’t want to use this on, but that’s easy. Using
verify straight away, I need to add the calls for each request method in
each controller, and if I change the behavior of failed verifications, I
have to modify all controllers.

I think I’ll keep using my approach as long as it serves its purpose and
doesn’t present any obstacles. If it does, I’ll step back and
re-evaluate.

Thanks again for your input Nathan. I really appreciate people taking
the time to share their knowledge.

Peace,
Phillip

The verify method would work like this:

verify :method => :get, :only => [:show, :new, :edit], :add_flash =>
{:error => ‘Invalid request method’}, :redirect_to => :error_path
verify :method => :post, :only => [:create], :add_flash => {:error =>
‘Invalid request method’}, :redirect_to => :error_path
verify :method => :put, :only => [:update], :add_flash => {:error =>
‘Invalid request method’}, :redirect_to => :error_path

Note that the urls are symbols in the filter because this is happening
at the class level where they are not defined. The symbols are later
turned into the appropriate url.