Rack::BodyParser
Rack middleware for parsing incoming request body per content type.
Inspiration
Rack::BodyParser
is heavily inspired by
Rack::Parser,
but behaves a bit different. See Key Features.
Key Features; Differences to Rack::Parser
-
Rack::BodyParser
only parses the request body. -
Rack::BodyParser
does not touch or replace the originalparams
orrequest.params
, and does not mixparams
and body payload. Instead the parsed result is stored in a separate keyparsed_body
in Rack'senv
. -
(optional) patching of
Rack::Request
with support for custom getter method Parsed result available asrequest.parsed_body
with support for customrequest.#{your_key_here}
per parser. Enable with:patch_request => true
. -
(optional) access to headers/env. If your parser
respond_to? :env=
, this setter method will be invoked with Rack'senv
just before runningcall
on your parser.
Batteries not included
Rack::BodyParser
does not contain any parsers out of the box.
Installation
Available through RubyGems:
gem install rack-bodyparser
Usage
Define your :parsers
per media-type
, which is the content-type
without media type parameters;
e.g., when content-type
is text/plain;charset=utf-8
, the media-type is text/plain
.
Parsers configuration accepts a String
or Regexp
as media-type key,
and anything that respond_to? 'call'
as parser.
Sinatra example:
# app.rb
use Rack::BodyParser, :parsers => {
'application/json' => proc { |data| JSON.parse data },
'application/xml' => proc { |data| XML.parse data },
/msgpack/ => proc { |data| Msgpack.parse data }
}
post '/' do
puts env['parsed_body']
end
Error Handling
Rack::BodyParser
has one default error handler that can be overridden by
setting the 'default' handler. As with parsers, you can use a String
or
Regexp
as media-type key and anything that respond_to? 'call'
as the
error handler. The error handler must accept the two parameters
Error
and type
(the media type).
use Rack::Parser, :handlers => {
'default' => proc { |e, type|
[400, {}, ['[Rack::BodyParser] Failed to parse %s: %s' % [type, e.to_s]]]
},
'application/xml' => proc { [400, {}, 'Error: XML unsupported'] }
}
Note: the error handler rescues exceptions that descend from StandardError
.
See http://www.mikeperham.com/2012/03/03/the-perils-of-rescue-exception/
Logging
Rack::BodyParser
will try to warn
if a logger
is present.
Patch Rack::Request
Setting up Rack::BodyParser
with :patch_request => true
will add
a parsed_body
getter method to Rack::Request
. Parsers can also provide a
:rack_request_key
to define a custom key per parser:
# gem 'jsonapi_parser'
require 'json/api' # JSONAPI document parser/validator
module Rack::BodyParser::JSONAPI
module Parser
# This defines the getter key for Rack::Request
def self.rack_request_key; :document; end
def self.call(body)
JSON::API.parse(JSON.parse(body))
end
end
module Error
def self.call(e, type)
payload = {
errors: {
title: 'Failed to parse body as JSONAPI document',
detail: e.message
}
}.to_json
[422, {}, [payload]]
end
end
end
use Rack::BodyParser, :patch_request => true,
:parsers => {
'application/vnd.api+json' => Rack::BodyParser::JSONAPI::Parser
},
:handlers => {
'application/vnd.api+json' => Rack::BodyParser::JSONAPI::Error
}
post '/' do
# These all output the same
puts env['parsed_body']
puts request.parsed_body
puts request.document
end
Request headers/environment
Need headers or other data from Rack's env
in your parser? Set up your parser
with a setter method, respond_to? :env=
, and it will be invoked just before running
the main call
method.
module ThisParserNeedsHeaders
def self.env=(env)
@env = env
end
def self.call(body)
# @env available here for your parsing pleasure.
end
end
use Rack::BodyParser, :parsers => {
'plain/text' => ThisParserNeedsHeaders
}
Copyright
Copyright © 2016, 2017 Aaron Heesakkers.
See MIT-LICENSE for details.