Skip to content

Instantly share code, notes, and snippets.

@steveworkman
Last active April 26, 2017 14:13
Show Gist options
  • Save steveworkman/7d490f4b25c181e2cccb7ab3f983774a to your computer and use it in GitHub Desktop.
Save steveworkman/7d490f4b25c181e2cccb7ab3f983774a to your computer and use it in GitHub Desktop.
Request.formData() is not implemented in Chrome, so here's a polyfill that will sort it out
// OK, Chrome (and all browsers apart from Firefox) don't support Request.formData(). So, we have to polyfill it
if (!Request.formData) {
Request.prototype.formData = function() {
return this.text().then(text => {
const fd = new FormData(),
contentType = this.headers.get('content-type');
// The text here may be a URIEncoded string, rather than multipart form
// Two different decoding strategies are needed
if (contentType.startsWith('multipart/form-data;')) {
// multipart/form-data encoding strategy
const boundaryKey = 'boundary=',
boundary = contentType.substr(contentType.indexOf(boundaryKey)+boundaryKey.length),
lines = text.split('\r\n');
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
// Spec says lines start with '--' and then the boundary in the header
// https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html
if ((line.startsWith('--'+boundary)) && i + 3 < lines.length) {
fd.append(lines[i + 1].match(/\"(.*)\"/)[1], lines[i + 3]);
}
}
} else if(contentType.startsWith('application/x-www-form-urlencoded;')) {
// Should be application/x-www-form-urlencoded. Use URLSearchParams for data
const params = new URLSearchParams(text);
for (const [key, val] of params) {
fd.append(key,val);
}
} else {
// There's also text/plain encoding. No strategy for that right now
throw TypeError(`Encoding ${contentType} not supported`);
}
return fd;
});
};
}
@jakearchibald
Copy link

Tidying up the use of promises, and throwing if the content-type isn't valid:

// OK, Chrome (and all browsers apart from Firefox) don't support Request.formData(). So, we have to polyfill it
if (!Request.formData) {
  Request.prototype.formData = function () {
    return this.text().then(text => {
      const fd = new FormData();
      const contentType = this.headers.get('content-type');

      // The text here may be a URIEncoded string, rather than multipart form
      // Two different decoding strategies are needed
      // Detection of the encoding type should be in the headers, but we can't assume that as requests
      // can be constructed without these things
      console.log(contentType);

      if (contentType.startsWith('multipart/form-data;')) {
        // multipart/form-data encoding strategy
        const boundaryKey = 'boundary=';
        const boundary = contentType.substr(contentType.indexOf(boundaryKey) + boundaryKey.length);
        const lines = text.split('\r\n');

        for (let i = 0; i < lines.length; i++) {
          const line = lines[i];
          if ((line.includes(boundary)) &&
            i + 3 < lines.length) {
            fd.append(lines[i + 1].match(/\"(.*)\"/)[1], lines[i + 3]);
          }
        }
      } else if (contentType.startsWith('application/x-www-form-urlencoded')) {
        // Should be application/x-www-form-urlencoded. Use URLSearchParams for data
        for (const [key, val] of new URLSearchParams(text)) {
          fd.append(key, val);
        }
      }
      else {
        throw TypeError(`Encoding ${contentType} not supported`);
      }

      return fd;
    });
  };
} 

@steveworkman
Copy link
Author

Updated gist with my latest version - comments updated, logs removed.

Thanks for the help Jake

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment