Multipart Types
Praxis does not only have multipart support for the design of your API, but it also provides some classes that allow you to read and generate proper multipart structures. The majority of that is still encapsulated in the MultipartArray
class, the same one used in the design pieces.
Multipart Responses
Praxis also provides support for generating multipart responses. In particular, Praxis provides:
- an
add_parts
accessor inPraxis::Response
to add parts to be returned in a response. - a
parts
accessor inPraxis::Response
to list the parts contained in a response. - a
Praxis::MultipartPart
class to represent and format individual parts
Here's an example of how to create a multipart response containing two parts named 'part1' and 'part2', oth use the 'text/plain' Content-Type. NOTE: We're assuming the structure of the multipart body to return is defined in a class elsewhere (i.e., MyDefinedMultipartResponseClass
), and that obviously the Parts we're adding will correspond and be valid according to that.
# This response directly defines a 200 status and a media_type of multipart/form-data
response = Praxis::Responses::MultipartOk.new
response.body = MyDefinedMultipartResponseClass.new
plain_headers = {'Content-Type' => 'text/plain'}
part1 = Praxis::MultipartPart.new("this is part 1", plain_headers)
response.add_part("part1", part1)
part2 = Praxis::MultipartPart.new("this is part 2", plain_headers)
response.add_part("part2", part2)
There is a specialized MultipartOk
response class is used to easily return a MultipartArray
body.
It just simply takes care of properly encoding the body as "multipart/form-data", with a proper "Content-Type"
header specifying the boundary of each part. The response is registered as :multipart_ok
.
Upon rendering, this response class will also take care of properly dumping each parts according to its "Content-Type" header, using any applicable handlers registered with Praxis. See Handlers
for more details on how to define and
register custom handlers.
Parsing multipart object
If we are trying to inspect an instance of a MultipartArray
(i.e., a request.payload
object when that's defined a one), we have two options:
- retrieve a part by name using
.part(name)
method. This usually results in a singleMultipartPart
instance. However, you will get an array ofMultipartPart
instances, if such part has been defined with themultiple: true
option - consider the object as an array of parts, and use the standard Ruby
Array
andEnumerable
methods. In this case you will get an instance ofMultipartPart
everytime (potentially with repeated part names if there were indeed multiple part with the same name).
Crafting MultipartArray objects
To create a MultipartArray
instance we can simply instantiate the object from the class, and start adding MultipartPart
instances through use push(part)
or push(*parts)
. Doing so it will validate the part names and coerce any headers and payload as applicable.
The MultipartPart
object have the following methods:
payload
: the part body dataheaders
: hash of headersname
: part namefilename
: filename, if applicable
Here you have an example action that shows how to read and genarate multipart payloads. This silly action will loop over the parts received in the action, and will generate an outgoing multipart response that has as many parts as received. Each of the generated parts will have a simple string body and a Special
header as well. Note that this assumes the bulk_create
action has been defined as receiving a MultipartArray
payload, which supports the .each
method to loop over the individual parts.
def bulk_create
self.response = Praxis::Responses::MultipartOk.new
# For demonstration purposes, reach out to the multipart type defined for this action
# and instantiate it here. Typically this type would probably be defined and reused from elsewhere
self.response.body = request.action.responses[:multipart_ok].media_type.new
# Loop over each incoming part, and generate an outgoing part for each
request.payload.each do |part_name, part|
part = Praxis::MultipartPart.new("the part body", name: part_name, {'Special' => 'header'})
response.add_part(part_name, part)
end
response
end