Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 6862

Show
Ignore:
Timestamp:
05/26/07 20:07:34 (1 year ago)
Author:
bitsweat
Message:

Routing: respond with 405 Method Not Allowed status when the route path matches but the HTTP method does not. Closes #6953.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/actionpack/CHANGELOG

    r6861 r6862  
    11*SVN* 
     2 
     3* Routing: respond with 405 Method Not Allowed status when the route path matches but the HTTP method does not.  #6953 [Josh Peek, defeated, Dan Kubb, Coda Hale] 
    24 
    35* Add support for assert_select_rjs with :show and :hide. #7780 [dchelimsky] 
  • trunk/actionpack/lib/action_controller/base.rb

    r6821 r6862  
    2222      @failures = failures 
    2323    end 
     24  end 
     25  class MethodNotAllowed < ActionControllerError #:nodoc: 
     26    attr_reader :allowed_methods 
     27 
     28    def initialize(*allowed_methods) 
     29      super("Only #{allowed_methods.to_sentence} requests are allowed.") 
     30      @allowed_methods = allowed_methods 
     31    end 
     32 
     33    def allowed_methods_header 
     34      allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' 
     35    end 
     36 
     37    def handle_response!(response) 
     38      response.headers['Allow'] ||= allowed_methods_header 
     39    end 
     40  end 
     41  class NotImplemented < MethodNotAllowed #:nodoc: 
    2442  end 
    2543  class UnknownController < ActionControllerError #:nodoc: 
  • trunk/actionpack/lib/action_controller/rescue.rb

    r6611 r6862  
    1414    DEFAULT_RESCUE_RESPONSE = :internal_server_error 
    1515    DEFAULT_RESCUE_RESPONSES = { 
    16       'ActionController::RoutingError'    => :not_found, 
    17       'ActionController::UnknownAction'   => :not_found, 
    18       'ActiveRecord::RecordNotFound'      => :not_found, 
    19       'ActiveRecord::StaleObjectError'    => :conflict, 
    20       'ActiveRecord::RecordInvalid'       => :unprocessable_entity, 
    21       'ActiveRecord::RecordNotSaved'      => :unprocessable_entity 
     16      'ActionController::RoutingError'     => :not_found, 
     17      'ActionController::UnknownAction'    => :not_found, 
     18      'ActiveRecord::RecordNotFound'       => :not_found, 
     19      'ActiveRecord::StaleObjectError'     => :conflict, 
     20      'ActiveRecord::RecordInvalid'        => :unprocessable_entity, 
     21      'ActiveRecord::RecordNotSaved'       => :unprocessable_entity, 
     22      'ActionController::MethodNotAllowed' => :method_not_allowed, 
     23      'ActionController::NotImplemented'   => :not_implemented 
    2224    } 
    2325 
     
    5658        log_error(exception) if logger 
    5759        erase_results if performed? 
     60 
     61        # Let the exception alter the response if it wants. 
     62        # For example, MethodNotAllowed sets the Allow header. 
     63        if exception.respond_to?(:handle_response!) 
     64          exception.handle_response!(response) 
     65        end 
    5866 
    5967        if consider_all_requests_local || local_request? 
  • trunk/actionpack/lib/action_controller/routing.rb

    r6783 r6862  
    251251    # TODO: , (comma) should be an allowed path character. 
    252252    SEPARATORS = %w( / ; . , ? ) 
     253 
     254    HTTP_METHODS = [:get, :head, :post, :put, :delete] 
    253255 
    254256    # The root paths which may contain controller files 
     
    13461348          result = route.recognize(path, environment) and return result 
    13471349        end 
    1348         raise RoutingError, "no route found to match #{path.inspect} with #{environment.inspect}" 
     1350 
     1351        allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, :method => verb) } } 
     1352 
     1353        if environment[:method] && !HTTP_METHODS.include?(environment[:method]) 
     1354          raise NotImplemented.new(*allows) 
     1355        elsif !allows.empty? 
     1356          raise MethodNotAllowed.new(*allows) 
     1357        else 
     1358          raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" 
     1359        end 
    13491360      end 
    13501361   
  • trunk/actionpack/test/controller/rescue_test.rb

    r6178 r6862  
    77    render :text => 'already rendered' 
    88    raise "don't panic!" 
     9  end 
     10 
     11  def method_not_allowed 
     12    raise ActionController::MethodNotAllowed.new(:get, :head, :put) 
     13  end 
     14   
     15  def not_implemented 
     16    raise ActionController::NotImplemented.new(:get, :put) 
    917  end 
    1018end 
     
    145153    assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid'] 
    146154    assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved'] 
     155    assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed'] 
     156    assert_equal :not_implemented, responses['ActionController::NotImplemented'] 
    147157  end 
    148158 
     
    176186      assert_nil @controller.send(:clean_backtrace, Exception.new) 
    177187    end 
     188  end 
     189   
     190  def test_not_implemented 
     191    with_all_requests_local false do 
     192      head :not_implemented 
     193    end 
     194    assert_response :not_implemented 
     195    assert_equal "GET, PUT", @response.headers['Allow'] 
     196  end 
     197 
     198  def test_method_not_allowed 
     199    with_all_requests_local false do 
     200      get :method_not_allowed 
     201    end 
     202    assert_response :method_not_allowed 
     203    assert_equal "GET, HEAD, PUT", @response.headers['Allow'] 
    178204  end 
    179205 
  • trunk/actionpack/test/controller/resources_test.rb

    r6807 r6862  
    189189      assert_restful_routes_for :messages do |options| 
    190190        assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get) 
    191         assert_raises(ActionController::RoutingError) do 
     191        assert_raises(ActionController::MethodNotAllowed) do 
    192192          ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post) 
    193193        end 
     
    385385      collection_path = "/#{controller_name}" 
    386386 
    387       assert_raises(ActionController::RoutingError) do 
     387      assert_raises(ActionController::MethodNotAllowed) do 
    388388        assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put) 
    389389      end 
    390390 
    391       assert_raises(ActionController::RoutingError) do 
     391      assert_raises(ActionController::MethodNotAllowed) do 
    392392        assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete) 
    393393      end 
  • trunk/actionpack/test/controller/routing_test.rb

    r6783 r6862  
    15911591    assert_equal("update", request.path_parameters[:action]) 
    15921592 
    1593     request.method = :update 
    1594     assert_raises(ActionController::RoutingError) { set.recognize(request) } 
     1593    begin 
     1594      request.method = :bacon 
     1595      set.recognize(request) 
     1596      flunk 'Should have raised NotImplemented' 
     1597    rescue ActionController::NotImplemented => e 
     1598      assert_equal [:get, :post, :put, :delete], e.allowed_methods 
     1599    end 
    15951600 
    15961601    request.path = "/people/5" 
     
    16091614    assert_equal("destroy", request.path_parameters[:action]) 
    16101615    assert_equal("5", request.path_parameters[:id]) 
    1611      
    1612     request.method = :post 
    1613     assert_raises(ActionController::RoutingError) { set.recognize(request) } 
    1614      
     1616 
     1617    begin 
     1618      request.method = :post 
     1619      set.recognize(request) 
     1620      flunk 'Should have raised MethodNotAllowed' 
     1621    rescue ActionController::MethodNotAllowed => e 
     1622      assert_equal [:get, :put, :delete], e.allowed_methods 
     1623    end 
     1624 
    16151625  ensure 
    16161626    Object.send(:remove_const, :PeopleController)