lib/roda/plugins/invalid_request_body.rb
# frozen-string-literal: true # class Roda module RodaPlugins # The invalid_request_body plugin allows for custom handling of invalid request # bodies. Roda uses Rack for parsing request bodies, so by default, any # invalid request bodies would result in Rack raising an exception, and the # exception could change for different reasons the request body is invalid. # This plugin overrides RodaRequest#POST (which parses parameters from request # bodies), and if parsing raises an exception, it allows for custom behavior. # # If you want to treat an invalid request body as the submission of no parameters, # you can use the :empty_hash argument when loading the plugin: # # plugin :invalid_request_body, :empty_hash # # If you want to return a empty 400 (Bad Request) response if an invalid request # body is submitted, you can use the :empty_400 argument when loading the plugin: # # plugin :invalid_request_body, :empty_400 # # If you want to raise a Roda::RodaPlugins::InvalidRequestBody::Error exception # if an invalid request body is submitted (which makes it easier to handle these # exceptions when using the error_handler plugin), you can use the :raise argument # when loading the plugin: # # plugin :invalid_request_body, :raise # # For custom behavior, you can pass a block when loading the plugin. The block # is called with the exception Rack raised when parsing the body. The block will # be used to define a method in the application's RodaRequest class. It can either # return a hash of parameters, or you can raise a different exception, or you # can halt processing and return a response: # # plugin :invalid_request_body do |exception| # # To treat the exception raised as a submitted parameter # {body_error: exception} # end module InvalidRequestBody # Exception class raised for invalid request bodies. Error = Class.new(RodaError) # Set the action to use (:empty_400, :empty_hash, :raise) for invalid request bodies, # or use a block for custom behavior. def self.configure(app, action=nil, &block) if action if block raise RodaError, "cannot provide both block and action when loading invalid_request_body plugin" end method = :"handle_invalid_request_body_#{action}" unless RequestMethods.private_method_defined?(method) raise RodaError, "invalid invalid_request_body action provided: #{action}" end app::RodaRequest.send(:alias_method, :handle_invalid_request_body, method) elsif block app::RodaRequest.class_eval do define_method(:handle_invalid_request_body, &block) alias handle_invalid_request_body handle_invalid_request_body end else raise RodaError, "must provide block or action when loading invalid_request_body plugin" end app::RodaRequest.send(:private, :handle_invalid_request_body) end module RequestMethods # Handle invalid request bodies as configured if the default behavior # raises an exception. def POST super rescue => e handle_invalid_request_body(e) end private # Return an empty 400 HTTP response for invalid request bodies. def handle_invalid_request_body_empty_400(e) response.status = 400 headers = response.headers headers.clear headers[RodaResponseHeaders::CONTENT_TYPE] = 'text/html' headers[RodaResponseHeaders::CONTENT_LENGTH] ='0' throw :halt, response.finish_with_body([]) end # Treat invalid request bodies by using an empty hash as the # POST params. def handle_invalid_request_body_empty_hash(e) {} end # Raise a specific error for all invalid request bodies, # to allow for easy rescuing using the error_handler plugin. def handle_invalid_request_body_raise(e) raise Error, e.message end end end register_plugin(:invalid_request_body, InvalidRequestBody) end end