Question

Multipart HTTP response

The goal is for a Node.js / hapi API server to respond to a browser's AJAX request with two things:

  • A media file (e.g. an image)
  • A JSON object with metadata about the file

These are two separate items only because binary data cannot easily be stored in JSON. Otherwise, this would be a single resource. Nevertheless, it is preferable that they be sent in a single response.

We upload these in a single request with multipart/form-data. In that case, browsers provide a built-in mechanism to serialize the body and most server-side frameworks know how to parse it. But how does one do the same for a response, in the opposite direction? Namely, how should a server serialize the body to transmit it to a client?

From what I can tell, multipart/mixed could be a useful content type. But there is very little talk of this. Most people seem to resort to providing two separate GET routes, one for each piece. I dislike that because it opens you up to race conditions, amongst other things. What am I missing?

See also my question in hapijs/discuss#563.

 46  38506  46
1 Jan 1970

Solution

 18

You can serve the response as multipart/form-data and use Response.formData() to read response at client

fetch("/path/to/server", {method:"POST", body:formData})
.then(response => response.formData())
.then(fd => {
  for (let [key, prop] of fd) {
    console.log(key, prop)
  }
})

let fd = new FormData();
fd.append("json", JSON.stringify({
  file: "image"
}));
fetch("data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH+GkNyZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQACgABACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkEAAoAAgAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkEAAoAAwAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkEAAoABAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQACgAFACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQACgAGACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAAKAAcALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==")
  .then(response => response.blob())
  .then(blob => {
    fd.append("file", blob);
    new Response(fd)
      .formData()
      .then(formData => {
        for (let [key, data] of formData) {
          console.log(key, data)
        }
      })
  })

2017-11-02

Solution

 4

If you are going for a multipart format, I don't think there's anything inherently wrong with using the exact same format both during upload (POST/PUT) and retrieval (GET).

I think there's definitely an elegance in using the same on-wire format in both directions when working with HTTP.

However, if you want to send form-data during PUT/POST and JSON back using GET, then I would start questioning whether this is the right thing to do.

multipart gets annoying for clients if they just want to display the image. Have you considered just using different endpoints; one for the image and one for it's meta-data? What reason do you have to want to combine them into a single resource?

Alternatively, you could also attempt to embed the information in the image. JPEG for instance allows custom data to be added using EXIF. At least you preserve the ability to just open the image directly.

However, I will conclude with saying that multipart/mixed is appropriate if you just want to embed an image + a json object, but bear in mind:

  1. It's probably a bit inconvenient for consumption
  2. It's also a bit unusual
  3. I'm fairly sure that multipart encoding will require you to encode your image in some 7bit encoding, which will inherently cause the request size to blow up quite a bit.
2017-11-02