Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

W3C HTML JSON form submission #1667

Closed
rianby64 opened this issue Aug 13, 2016 · 12 comments
Closed

W3C HTML JSON form submission #1667

rianby64 opened this issue Aug 13, 2016 · 12 comments

Comments

@rianby64
Copy link

Hello WHATWG! I'm reading the section for enctype and found that there's no state for application/json or something similar that allows form submission in JSON.

I ignore the reasons that forced W3C to stop maintaining that specification. But I'm wondering if that spec worths or not?

Thanks for your time.

@annevk
Copy link
Member

annevk commented Aug 14, 2016

It's a lot of added complexity for little value. And there is no implementer interest because of that.

@rianby64
Copy link
Author

If I want to convert a form into an object, I may implement a simple solution... something like

      // convert the form into an object
      var formObj = {};
      for (var i = 0; i < form.length; i++) {
        if (form[i].id) {
          formObj[form[i].id] = form[i].value;
        }
      }

@annevk, thanks for the information.

@annevk
Copy link
Member

annevk commented Aug 14, 2016

You can also use FormData(form). Perhaps we should expose a toJSON on FormData. That might be fairly reasonable.

@rianby64
Copy link
Author

Hmm... sounds good.
Here's my current version, without toJSON()

    form.addEventListener('submit', e => {
      e.preventDefault();

      var formObj = {};
      var fd = new FormData(e.target);
      for (var item of fd) {
        formObj[item[0]] = item[1];
      }

      fetch(`/api/category/${formObj.id}`, {
        method: 'put',
        headers: new Headers({
          'Content-Type': 'application/json'
        }),
        body: JSON.stringify(formObj)
      });
    });

Let me dream a bit, please...
With toJSON()

    form.addEventListener('submit', e => {
      e.preventDefault();

      var fd = new FormData(e.target).toJSON();
      fetch(`/api/category/${formObj.id}`, {
        method: 'put',
        body: fd  // then the fetch process should guess the content-type
      });
    });

Or just by setting the content-type

    form.addEventListener('submit', e => {
      e.preventDefault();

      var fd = new FormData(e.target);
      fetch(`/api/category/${formObj.id}`, {
        method: 'put',
        headers: new Headers({
          'Content-Type': 'application/json'
        }),
        body: fd // the fetch process will call toJSON automatically
      });
    });

The main goal that I see here is the possibility to expose the form depending on the content-type.

@domenic
Copy link
Member

domenic commented Aug 16, 2016

@annevk do you want to move this to XHR to add a toJSON() and close this issue?

@annevk
Copy link
Member

annevk commented Aug 16, 2016

I opened whatwg/xhr#84 though given your comments in whatwg/url#143 I wonder whether returning a JSON object here is the way to go.

@annevk
Copy link
Member

annevk commented Aug 16, 2016

@rianby64 if you're satisfied with this resolution please close this issue. Your last idea goes a tad too far I think. We don't really want browsers to inspect the headers to determine how to serialize a value. That's too much action-at-a-distance.

@rianby64
Copy link
Author

So, the first variant seems to be better than the second one. Nice 😄 ! Thanks a million!

@AMorgaut
Copy link

AMorgaut commented Jan 25, 2017

Coming a bit late (6 month) to this topic (just saw it), but would like to share my view on application/json as "enctype" attribute value

JSON, form URL encoded, and plain text

JSON encoding is more or less like the "x-www-form-urlencoded" format. It is not designed to embed binary data. As such, both the application/x-www-form-urlencoded serializer and the text/plain encoding algorythm says that they serialize "file" entries in form data by replacing their value with their file's name only. Base64 encoding has been estimated as a no go for files use case (to much overhead).

So I think it would be fair to says that either native XHR/fetch support or form data toJSON() method should behave the same.

What for?

Well

  • URL encoding/decoding entry values can get a real cost for non ascii characters (UTF-8 is usually encoded in percent values), while JSON support utf-8 from start.
  • values types could be better represented (see the next "What for?" section at the end of this comment)

Multipart, form-data, and JSON

The way we send files over XMLHttpRequest / Fetch is either sending directly a File/Blob/ArrayBuffer as the main body or via a FormData, with files entries, using the "multipart/form-data" encoding.
"multipart" is the MIME type standard to mix binary, plain text, and any formatted data in "parts" joined in a single body.

So if we would like to send Form entries including files using JSON encoding, the only approach I would imagine is defining a "multipart/json-form-data" or more easily "multipart/form-data+json" (standard way to indicate a JSON flavor of an existing format. It would look very much like classic "form-data" but may benefit from few JSON formating advantages.

In multipart/form-data MIME format, each Form entry becomes a MIME part with its own Headers.

The main "part header" is Content-Disposition. (ex: Content-Disposition: form-data; name="field1"). It provides the form entry name. If the entry is a file, it cans contain also the file name (ex: Content-Disposition: form-data; name="myFile"; filename="summary.pdf")

The second main "part header" is Content-Type, which default value is "text/plain" if not provided (as for standard input entries), but this value is important for files. It can have a specific MIME type, if detected, based on the embedded file (ex: application/pdf). By default it will be "application/octet-stream".

In a JSON flavor, "non-file" parts could have their "Content-type" set to "application/json"

What for?

  • HTML5 form entries having a specific type like "number" could be differentiated from the text related ones like "text" (default), "search", or "url"
  • "checkbox" values could be represented as real boolean values (if no text value is specified, Chrome returns me the text "on", if it is checked, in classic the form-data format)
  • entries with multiple values (<select>, "radio" type, or other <input> entries with the multiple attribute) could be grouped in a single part using the array JSON notation (for non-binary ones)

That's all... Just wanted to share what kind of bell this feature request did ring to me.

Still, I agree, server side frameworks usually work pretty well with classic form data... But well, getting automatically the right value type would be an extra I often felt was missing.

PS: of course another thing is to consider if such "enctype" is acceptable with both GET/POST methods, or only for POST ones (like multipart/form-data), potentially because some HTTP client/proxy/server may not support JSON in URLs if not URL encoded...

@caub
Copy link

caub commented Oct 14, 2018

I also think it would be useful to make enctype="application/json" work

<form method="post" action="https://httpbin/post" enctype="application/json">
  <input name="foo" value="bar">
  <input type="submit">
</form>

https://jsfiddle.net/crl/cj9f0qna/ foo=bar is sent when submitting, instead of {"foo":"bar"} if enctype="application/json" were implemented

@annevk
Copy link
Member

annevk commented Oct 15, 2018

The problem is that any on-the-wire extension to forms requires navigation to become CORS-aware, which would be rather involved and with lots of details to think through. Coupled with lack of active implementer interest it doesn't really seem like something that's going to happen anytime soon.

Perhaps once navigation is refactored better so that it's more clear what the consequences would be.

@caub
Copy link

caub commented Nov 5, 2021

for the record @rianby64, just do:

fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(Object.fromEntries(new FormData(form)))
});

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

No branches or pull requests

5 participants