POST request to encode data (part 2)

There is no way, because the article is too long, nuggets on a one-time display can not be divided into two parts to send, we did not read the top part, remember to read the half part

Ok, let’s continue to decrypt the last half of the question…

  • POST request to encode data (part 2)
    • FormData
      • Let FormData be the body of the Fetch
      • Convert URLSearchParams
      • Read the body of the Fetch as FormData
    • Other formats that can be used as the body of the Fetch
      • Blobs
      • Strings
      • Buffers
      • Streams
    • One final bonus: Convert FormData to JSON
  • reference
    • Finally, welcome to my official account: Joshua, the front end upperclassman

FormData

A FormData object can represent a set of key-value data for an HTML form. The key can also be a file, as in .

You can add data directly to the FormData object:

const formData = new FormData();
formData.set('foo', 'bar');
formData.set('hello', 'world');
Copy the code

The FormData object is also an iterator, so you can convert it to a key-value pair array or object, just as you would with URLSearchParams. However, unlike URLSearchParams, you can read HTML forms directly as FormData:

const formElement = document.querySelector('form');
const formData = new FormData(formElement);
console.log(formData.get('username'));
Copy the code

This way, you can easily get the data from the form. I use this approach a lot, and find it much easier than getting the data from each element individually.

Let FormData be the body of the Fetch

Like URLSearchParams, you can use FormData directly as the fetch body:

const formData = new FormData();
formData.set('foo', 'bar');
formData.set('hello', 'world');

fetch(url, {
  method: 'POST',
  body: formData,
});
Copy the code

This automatically sets the Content-Type header to multipart/form-data and sends the data in this format:

const formData = new FormData();
formData.set('foo', 'bar');
formData.set('hello', 'world');

const request = new Request('', { method: 'POST', body: formData });
console.log(await request.text());
Copy the code

. Console. log displays the following information:

------WebKitFormBoundaryUekOXqmLphEavsu5
Content-Disposition: form-data; name="foo"

bar
------WebKitFormBoundaryUekOXqmLphEavsu5
Content-Disposition: form-data; name="hello"

world
------WebKitFormBoundaryUekOXqmLphEavsu5--
Copy the code

This is what the body looks like when sending data in multipart/form-data format. It is more complex than Application/X-www-form-urlencoded, but it can contain file data. However, some servers cannot handle multipart/form-data, such as Express. If you want to support multipart/form-data in Express, you’ll need to use libraries like Busboy or Formidable to help

But what if you want to send forms as Application/X-www-form-urlencoded? B: well…

Convert URLSearchParams

Because the URLSearchParams constructor can accept an iterator that generates key-value pairs, and FormData’s iterator does just that, it generates key-value pairs, so you can convert FormData to URLSearchParams:

const formElement = document.querySelector('form');
const formData = new FormData(formElement);
const searchParams = new URLSearchParams(formData);

fetch(url, {
  method: 'POST',
  body: searchParams,
});
Copy the code

However, this conversion process throws an error if the form data contains file data. Because Application/X-www-form-urlencoded cannot represent file data, neither can URLSearchParams.

Read the body of the Fetch as FormData

You can also read a Request or Response object as FormData:

const formData = await request.formData();
Copy the code

This works well if the body of the Request or Response is multipart/form-data or application/ X-www-form-urlencoded. It is especially useful for handling form submissions in the server.

Other formats that can be used as the body of the Fetch

There are a few other formats that can be used as the body of a Fetch:

Blobs

A Blob object (File can also be the body of a Fetch, since it inherits from Blob) can be the body of a Fetch:

fetch(url, {
  method: 'POST',
  body: blob,
});
Copy the code

This automatically sets the content-type to the value of blob.type.

Strings

fetch(url, {
  method: 'POST',
  body: JSON.stringify({ hello: 'world' }),
  headers: { 'Content-Type': 'application/json' },
});
Copy the code

This automatically sets the content-type to text/plain; Charset =UTF-8, but it can be overridden, as I did above, by setting the Content-Type to Application /json

Buffers

ArrayBuffer objects, and anything supported by array buffers, such as Uint8Array, can be used as the body of a Fetch:

Fetch (URL, {method: 'POST', body: new Uint8Array([//... ), headers: { 'Content-Type': 'image/png' }, });Copy the code

This does not automatically set the Content-Type field, so you need to do it yourself.

Streams

Finally, the fetch body can be a stream! This allows the server to have a different development experience for Response objects, and they can also be used with Request.

So don’t try to do multipart/form-data or Application/X-ww-form-urlencoded data yourself, let FormData and URLSearchParams do the hard work for us!

One final bonus: Convert FormData to JSON

Now, there’s a problem, which is:

How do I serialize FormData to JSON without losing the data?

Forms can contain fields like this:

<select multiple name="tvShows"> <option>Motherland</option> <option>Taskmaster</option>... </select>Copy the code

Of course, you can select multiple values, or you can have multiple inputs with the same name:

<fieldset> <legend>TV Shows</legend> <label> <input type="checkbox" name="tvShows" value="Motherland" /> Motherland </label> <label> <input type="checkbox" name="tvShows" value="Taskmaster" /> Taskmaster </label>... </fieldset>Copy the code

The result is a FormData object with multiple fields of the same name, as shown below:

const formData = new FormData();
formData.append('foo', 'bar');
formData.append('tvShows', 'Motherland');
formData.append('tvShows', 'Taskmaster');
Copy the code

As we saw in URLSearchParams, some object conversions are lossy (some attributes are culled) :

// { foo: 'bar', tvShows: 'Taskmaster' }
const data = Object.fromEntries(formData);
Copy the code

There are several ways to avoid data loss and still end up serializing fromData data to JSON.

First, convert the array to [key, value] :

// [['foo', 'bar'], ['tvShows', 'Motherland'], ['tvShows', 'Taskmaster']]
const data = [...formData];
Copy the code

But if you want to convert to an object instead of an array, you can do this:

const data = Object.fromEntries(
  // Get a de-duped set of keys
  [...new Set(formData.keys())]
    // Map to [key, arrayOfValues]
    .map((key) => [key, formData.getAll(key)]),
);
Copy the code

. The data variable of the appeal code, finally, is:

{
  "foo": ["bar"],
  "tvShows": ["Motherland", "Taskmaster"]
}
Copy the code

I prefer each value in the data to be an array, even if it has only one item. This prevents a lot of code branching on the server and simplifies validation. Although, you might prefer the PHP/Perl convention, where the field name ending in [] means “this should generate an array”, as follows:

TvShows < select multiple name = "[]" >... </select>Copy the code

And let’s convert it:

const data = Object.fromEntries(
  // Get a de-duped set of keys
  [...new Set(formData.keys())].map((key) =>
    key.endsWith('[]')
      ? // Remove [] from the end and get an array of values
        [key.slice(0, -2), formData.getAll(key)]
      : // Use the key as-is and get a single value
        [key, formData.get(key)],
  ),
);
Copy the code

. The data variable of the appeal code, finally, is:

{
  "foo": "bar",
  "tvShows": ["Motherland", "Taskmaster"]
}
Copy the code

Note: If the form contains file data, do not attempt to convert the form to JSON. If this is the case where the form contains file data, multipart/form-data is much better.

reference

  • Encoding data for POST requests

Finally, welcome to my official account: Joshua, the front end upperclassman