4

Всякий раз, когда вы отправляете неправильный JSON против моего API-интерфейса Rails 5.x, я получаю исключение, а Rails возвращает всю трассировку стека как JSON. Очевидно, что я хотел бы ответить с красивой, настраиваемой, отформатированной ошибкой.Как спасти ActionDispatch :: ParamsParser :: ParseError и вернуть пользовательскую ошибку API в рельсах 5?

=> Booting Puma 
=> Rails 5.0.0.1 application starting in development on http://localhost:3000 
=> Run `rails server -h` for more startup options 
Puma starting in single mode... 
* Version 3.6.0 (ruby 2.3.0-p0), codename: Sleepy Sunday Serenity 
* Min threads: 5, max threads: 5 
* Environment: development 
* Listening on tcp://localhost:3000 
Use Ctrl-C to stop 
Started POST "/api/v1/identities/" for ::1 at 2016-10-26 18:42:32 +0200 
    ActiveRecord::SchemaMigration Load (0.3ms) SELECT "schema_migrations".* FROM "schema_migrations" 
Error occurred while parsing request parameters. 
Contents: 

{ 
    "whatever": "ewewgewewtwe" 
    "malformed_json": "; 
} 

ActionDispatch::ParamsParser::ParseError (822: unexpected token at '{ 
    "whatever": "ewewgewewtwe" 
    "malformed_json": "; 
}'): 

actionpack (5.0.0.1) lib/action_dispatch/http/parameters.rb:71:in `rescue in parse_formatted_parameters' 
actionpack (5.0.0.1) lib/action_dispatch/http/parameters.rb:65:in `parse_formatted_parameters' 
actionpack (5.0.0.1) lib/action_dispatch/http/request.rb:366:in `block in POST' 
rack (2.0.1) lib/rack/request.rb:57:in `fetch' 
rack (2.0.1) lib/rack/request.rb:57:in `fetch_header' 
actionpack (5.0.0.1) lib/action_dispatch/http/request.rb:365:in `POST' 
actionpack (5.0.0.1) lib/action_controller/metal/params_wrapper.rb:282:in `_wrapper_enabled?' 
actionpack (5.0.0.1) lib/action_controller/metal/params_wrapper.rb:231:in `process_action' 
activerecord (5.0.0.1) lib/active_record/railties/controller_runtime.rb:18:in `process_action' 
actionpack (5.0.0.1) lib/abstract_controller/base.rb:126:in `process' 
actionpack (5.0.0.1) lib/action_controller/metal.rb:190:in `dispatch' 
actionpack (5.0.0.1) lib/action_controller/metal.rb:262:in `dispatch' 
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:50:in `dispatch' 
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:32:in `serve' 
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:39:in `block in serve' 
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:26:in `each' 
actionpack (5.0.0.1) lib/action_dispatch/journey/router.rb:26:in `serve' 
actionpack (5.0.0.1) lib/action_dispatch/routing/route_set.rb:725:in `call' 
rack (2.0.1) lib/rack/etag.rb:25:in `call' 
rack (2.0.1) lib/rack/conditional_get.rb:38:in `call' 
rack (2.0.1) lib/rack/head.rb:12:in `call' 
activerecord (5.0.0.1) lib/active_record/migration.rb:552:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/callbacks.rb:38:in `block in call' 
activesupport (5.0.0.1) lib/active_support/callbacks.rb:97:in `__run_callbacks__' 
activesupport (5.0.0.1) lib/active_support/callbacks.rb:750:in `_run_call_callbacks' 
activesupport (5.0.0.1) lib/active_support/callbacks.rb:90:in `run_callbacks' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/callbacks.rb:36:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/executor.rb:12:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/remote_ip.rb:79:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/debug_exceptions.rb:49:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/show_exceptions.rb:31:in `call' 
railties (5.0.0.1) lib/rails/rack/logger.rb:36:in `call_app' 
railties (5.0.0.1) lib/rails/rack/logger.rb:24:in `block in call' 
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:70:in `block in tagged' 
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:26:in `tagged' 
activesupport (5.0.0.1) lib/active_support/tagged_logging.rb:70:in `tagged' 
railties (5.0.0.1) lib/rails/rack/logger.rb:24:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/request_id.rb:24:in `call' 
rack (2.0.1) lib/rack/runtime.rb:22:in `call' 
activesupport (5.0.0.1) lib/active_support/cache/strategy/local_cache_middleware.rb:28:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/executor.rb:12:in `call' 
actionpack (5.0.0.1) lib/action_dispatch/middleware/static.rb:136:in `call' 
rack (2.0.1) lib/rack/sendfile.rb:111:in `call' 
railties (5.0.0.1) lib/rails/engine.rb:522:in `call' 
puma (3.6.0) lib/puma/configuration.rb:225:in `call' 
puma (3.6.0) lib/puma/server.rb:578:in `handle_request' 
puma (3.6.0) lib/puma/server.rb:415:in `process_client' 
puma (3.6.0) lib/puma/server.rb:275:in `block in run' 
puma (3.6.0) lib/puma/thread_pool.rb:116:in `block in spawn_thread' 

Раньше я думаю, что можно было добавить промежуточное программное обеспечение и обработать исключение следующим образом:

# in config/application.rb 

config.middleware.insert_before(ActionDispatch::ParamsError,'BadRequestError') 

# middleware 

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

    def call(env) 
     begin 
      @app.call(env) 
     rescue ActionDispatch::ParamsParser::ParseError 
      Api::ApiController.action(:raise_bad_request).call(env) 
     end 
    end 
end 

Однако, кажется, что промежуточное ActionDispatch::ParamsError был удален из Rails 5;

Я также пробовал с другими посредниками (например, ActionDispatch :: ShowExceptions), спасая разные ошибки, но мое действие raise_bad_request так или иначе не вызывалось.

Я пропустил что-то, что-то не так, или есть другой способ сделать это с Rails 5?

Спасибо!

ответ

8

Обновлен ответ на работу с Rails 5.1 и новее. (Спасибо @Edwin Meyer) config.middlware.use нужен класс вместо строки.

Также я упоминаю сейчас, что я установил промежуточное ПО под app/middleware. Так что не нужно требовать.


У меня была та же проблема, и это было очень просто решить в моем случае.

Просто используйте в application.rb:

config.middleware.use CatchJsonParseErrors 

# Instead of 
# config.middleware.insert_before ActionDispatch::ParamsParser, "CatchJsonParseErrors" 
# I used in my Rails 4 app 

Мой промежуточного слоя выглядит следующим образом (от Catching Invalid JSON Parse Errors with Rack Middleware):

# app/middleware/catch_json_parse_errors.rb 

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

    def call(env) 
    begin 
     @app.call(env) 
    rescue ActionDispatch::ParamsParser::ParseError => error 
     if env['HTTP_ACCEPT'] =~ /application\/json/ 
     error_output = "There was a problem in the JSON you submitted: #{error}" 
     return [ 
      400, { "Content-Type" => "application/json" }, 
      [ { status: 400, error: error_output }.to_json ] 
     ] 
     else 
     raise error 
     end 
    end 
    end 
end 

Тогда, когда я отправить недопустимый JSON ответ выглядит следующим образом:

{"status":400,"error":"There was a problem in the JSON you submitted: 743: unexpected token at '{ \"foo\": \"bar\" '"} 

Необходимо, чтобы вы указали правильные ACCEPT.

Надеюсь, это вам тоже поможет.

2

Я бег Rails 5.1.4, и я нашел следующую необходимую для получения решения спа-салона модификации для работы для меня:

В application.rb -

1) аргумент в config.middleware .use, кажется, класс, а не строка: должна быть обязательно config.middleware.use CatchJsonParseErrors
2) catch_json_parse_errors.rb:
require './app/middleware/catch_json_parse_errors'

в catch_json_parse_errors.гь -
Изменения линии
if env['HTTP_ACCEPT'] =~ /application\/json/
в
if env['HTTP_ACCEPT'] =~ /application\/json/ || env['CONTENT_TYPE'] =~ /application\/json/
так плохо содержательных запросов HTTP, где указан CONTENT_TYPE, но не HTTP_ACCEPT также обрабатывается.

Смежные вопросы