Search code examples
elixirphoenix-frameworkplug

Parsing a json part of a multipart/mixed request in Elixir/Phoenix


I'm writing an Elixir/Phoenix json API backend and would like an endpoint to receive multiple image files along with some json-formatted attributes in a single request. I'm using the multipart/mixed content type for that and here is what my cURL request looks like.

curl -4 -H "Content-Type: multipart/mixed" \
  -H "Authorization: FNRM8ISXiUujh+I2X" \
  -F "meta={"registration": "face"}; type=application/json" \
  -F "captures[][email protected]; type=image/jpg" \
  -F "captures[][email protected]; type=image/jpg" \
  -X POST http://localhost:4000/api/bioid/register -i -v

In my controller action I receive neatly parsed files but my json part is left unparsed.

pry(2)> params
%{
  "captures" => [
    %Plug.Upload{
      content_type: "image/jpg",
      filename: "r1.jpg",
      path: "/var/folders/v7/qlf90zb13szch162hvx6d1s00000gn/T//plug-1530/multipart-1530206341-21611204340048-7"
    },
    %Plug.Upload{
      content_type: "image/jpg",
      filename: "r2.jpg",
      path: "/var/folders/v7/qlf90zb13szch162hvx6d1s00000gn/T//plug-1530/multipart-1530206341-700143184604965-7"
    }
  ],
  "meta" => "{registration: face}; type=application/json"
}

Here is a discussion that seems relevant, WIP: How to parse multipart form with JSON field #570. It makes it apparent that the parsing of the application/json parts should not be expected to happen automatically. Instead the last message suggests using a custom body reader to perform any extra parsing.

Is this interpretation correct or is there another way to handle the multipart/mixed payload so that any params originated from application/json parts would be available in the controller action already parsed?


A side question

What confuses me is for the parsing above to happen I don't even have to use Plug.Parsers. From what I've read, the following should be necessary to get a parsed multipart payload.

plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  json_decoder: Poison

I get identical results whether this plug is present in the pipeline or not. It probably is there by default but I'd like to confirm that.

I'm using Elixir 1.6.5, Phoenix 1.3.2 and Plug 1.6.0.


Solution

  • The Plug multipart parser seems to work as expected. The Plug JSON parser apparently only works for requests with a content type of application/json or application/*+json, see source code. I assume the parsers are looking at the content type of the entire request and are not aware of the multipart request parts.

    As far as I can see, the currently simplest solution would be to parse the content yourself. I can see how you expected this to work though and it might be worth to request this feature via Github issue.