Rails ActionController::BadRequest causes 500 Server Error on production server

左心房为你撑大大i 提交于 2019-12-22 03:44:58

问题


We have a Turkish website and some old links are crawled by some search engines. The links seem to be malformed or cannot be processed and therefore causing ActionController::BadRequest error. On the local machine with development env this causes to return the Rails error page with ActionController::BadRequest.

But on the Server we get an 500 server error. This issues is discussed on several other pages like here. But none of the solutions helped.

In both cases we would like to redirect to a page not found-page.

I already tried to rescue_from ActionController::BadRequest and rescue_from ActionController::RoutingError in ApplicationController because of the aforementioned article, where they state that BadRequest turns into RoutingError.

But neither of them worked.

I hope someone had the same problem and already resolved it. Thanks in advance for your answers.

Edit:

One example problem-url is http://localhost:3000/Di%c5%ef%bf%bd-f%c4%b1r%c3%a7as%c4%b1.

Terminal output:

ActionController::BadRequest (ActionController::BadRequest):
  actionpack (4.0.0) lib/action_dispatch/routing/route_set.rb:37:in `block in call'
  actionpack (4.0.0) lib/action_dispatch/routing/route_set.rb:33:in `each'
  actionpack (4.0.0) lib/action_dispatch/routing/route_set.rb:33:in `call'
  actionpack (4.0.0) lib/action_dispatch/journey/router.rb:71:in `block in call'
  actionpack (4.0.0) lib/action_dispatch/journey/router.rb:59:in `each'
  actionpack (4.0.0) lib/action_dispatch/journey/router.rb:59:in `call'
  actionpack (4.0.0) lib/action_dispatch/routing/route_set.rb:655:in `call'
  newrelic_rpm (3.6.4.122) lib/new_relic/rack/error_collector.rb:12:in `call'
  newrelic_rpm (3.6.4.122) lib/new_relic/rack/agent_hooks.rb:22:in `call'
  newrelic_rpm (3.6.4.122) lib/new_relic/rack/browser_monitoring.rb:16:in `call'
  newrelic_rpm (3.6.4.122) lib/new_relic/rack/developer_mode.rb:28:in `call'
  rack (1.5.2) lib/rack/etag.rb:23:in `call'
  rack (1.5.2) lib/rack/conditionalget.rb:25:in `call'
  rack (1.5.2) lib/rack/head.rb:11:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/params_parser.rb:27:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/flash.rb:241:in `call'
  rack (1.5.2) lib/rack/session/abstract/id.rb:225:in `context'
  rack (1.5.2) lib/rack/session/abstract/id.rb:220:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/cookies.rb:486:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call'
  activesupport (4.0.0) lib/active_support/callbacks.rb:373:in `_run__4278100521352222029__call__callbacks'
  activesupport (4.0.0) lib/active_support/callbacks.rb:80:in `run_callbacks'
  actionpack (4.0.0) lib/action_dispatch/middleware/callbacks.rb:27:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/reloader.rb:64:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/remote_ip.rb:76:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call'
  rollbar (0.11.7) lib/rollbar/middleware/rails/show_exceptions.rb:19:in `call_with_rollbar'
  actionpack (4.0.0) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'
  railties (4.0.0) lib/rails/rack/logger.rb:38:in `call_app'
  railties (4.0.0) lib/rails/rack/logger.rb:21:in `block in call'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `block in tagged'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:25:in `tagged'
  activesupport (4.0.0) lib/active_support/tagged_logging.rb:67:in `tagged'
  railties (4.0.0) lib/rails/rack/logger.rb:21:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/request_id.rb:21:in `call'
  rack (1.5.2) lib/rack/methodoverride.rb:21:in `call'
  rack (1.5.2) lib/rack/runtime.rb:17:in `call'
  activesupport (4.0.0) lib/active_support/cache/strategy/local_cache.rb:83:in `call'
  rack (1.5.2) lib/rack/lock.rb:17:in `call'
  actionpack (4.0.0) lib/action_dispatch/middleware/static.rb:64:in `call'
  railties (4.0.0) lib/rails/engine.rb:511:in `call'
  railties (4.0.0) lib/rails/application.rb:97:in `call'
  rack (1.5.2) lib/rack/content_length.rb:14:in `call'
  puma (2.6.0) lib/puma/server.rb:486:in `handle_request'
  puma (2.6.0) lib/puma/server.rb:357:in `process_client'
  puma (2.6.0) lib/puma/server.rb:250:in `block in run'
  puma (2.6.0) lib/puma/thread_pool.rb:92:in `call'
  puma (2.6.0) lib/puma/thread_pool.rb:92:in `block in spawn_thread'

Best regards


回答1:


I had the exact same issue in a Rails 4.0.x app where it was polluting my New Relic error page.

I got around this by writing a middleware that caches the ActionController::BadRequest error, Logs it and returns a 400 error page. (A 400 seemed more appropriate then a 404 error.)

app/middleware/catch_request_errors.rb

class CatchRequestErrors
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      @app.call(env)
    rescue ActionController::BadRequest => error
      ::Rails.logger.warn("WARN: 400 ActionController::BadRequest: #{env['REQUEST_URI']}")
      @html_400_page ||= File.read(::Rails.root.join('public', '400.html'))
      [
          400, { "Content-Type" => "text/html" },
          [ @html_400_page ]
      ]
    end
  end
end

config/application.rb

config.middleware.insert_before ActionDispatch::ParamsParser, "CatchRequestErrors"

public/400.html

<!DOCTYPE html>
<html>
<head>
  <title>Your request could not be handled (400)</title>
  <style type="text/css">
    body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
    div.dialog {
      width: 25em;
      padding: 0 4em;
      margin: 4em auto 0 auto;
      border: 1px solid #ccc;
      border-right-color: #999;
      border-bottom-color: #999;
    }
    h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
  </style>
</head>

<body>
  <!-- This file lives in public/400.html -->
  <div class="dialog">
    <h1>Your request could not be handled.</h1>
    <p>Please check the url and post data for syntax errors.</p>
  </div>
</body>
</html>

This stops processing the rails stack, logs the error and returns the 400.html page to the user freeing the app to process a more valid request.

I'm also caching the 400 page as a instance variable to save on GC and Disc IO.




回答2:


There is a hack. Put this code inside initializers

module Rack
  module Utils
    alias_method :original_normalize_params, :normalize_params
    module_function :original_normalize_params

    def normalize_params(params, name, v = nil)
      begin
        original_normalize_params(params, name, v)
      rescue => e
        raise ActionController::BadRequest.new("Incorrect URL")
      end
    end

    module_function :normalize_params
  end
end

It will respond with 400 for requests like http://127.0.0.1:3000/?foo[]=array&foo[hash]=hash

EDIT:

Also, it is possible to implement middleware which checks for correctness of parameters.

Catching bad queries by middleware

# config/application.rb

require File.expand_path('../../lib/query_validator', __FILE__)

module MyApp
  class Application < Rails::Application
    # configurations

    config.middleware.insert_before('ActionDispatch::ShowExceptions', QueryValidator)
  end
end

# lib/query_validator.rb

class QueryValidator
  def initialize(app)
    @app = app
  end

  def call(env)
    begin
      Rack::Utils.parse_nested_query(env['QUERY_STRING'])

      env['QUERY_STRING'].valid_encoding? or
        raise ActionController::BadRequest, "Invalid parameter: #{env['QUERY_STRING']}"
    rescue => e
      env['QUERY_STRING'] = ''
      env['my_app.query_errors'] = 'Invalid query.'
    end

    @app.call(env)
  end
end


# application_controller.rb
class ApplicationController < ActionController::Base
  before_filter do
    if env['my_app.query_errors']
      flash[:alert] = env['my_app.query_errors']
      redirect_to root_path
    end
  end

end



回答3:


I suppose that one of your routes or Rack middleware is invalid and causes the 500 error. In new Rails app there is no incorrect behaviour with url http://localhost:3000/Di%c5%ef%bf%bd-f%c4%b1r%c3%a7as%c4%b1 - app returns a 404 error as expected. Also request to http://127.0.0.1:3000/?foo[]=array&foo[hash]=hash return absolutely correct response with 400 error (BadRequest).

Try to rewrite routes like this:

Rails.application.routes.draw do
  get '/*path', :to => lambda { |env| [200, {}, [env.to_s]]}
  # omited
end

and take a look to response status - if it 200, than problem in your Rails application, otherwise anywhere else in the rack middleware.

rails -v #=> Rails 4.1.1



来源:https://stackoverflow.com/questions/23954324/rails-actioncontrollerbadrequest-causes-500-server-error-on-production-server

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!