Form data encoding and decoding -- encodeURIComponent, URLSearchParams, FormData

This article mainly explains how to deal with various parameter problems through web api to prevent security problems and more convenient operation.

Let's take a look at an example:

const response = await fetch(url, {
  method: 'POST',
  body: `text=${text}`,
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  }
})
const json = await response.json()

The above code will have some "security problems", such as SQL or HTML injection through text=${text}.

Before we start, let's list the "content type - content type" often used in our daily development, which is used to specify the MIME type of resources media type , define the type of network file and the code of web page, and decide what form and code the browser will read the file.

Content type common typesexplain
application/x-www-form-urlencodedBy default, form data
multipart/form-dataForm data (can contain file data)
application/jsonjson data format
image/pngpng picture format
text/htmlHTML format
text/plainPlain text format

For more types, please refer to MIME types list

encodeURIComponent

< form > the default format of form request is x-www-form-urlencoded, which converts the data in the form into key value pairs, such as title =% E4% BD% A0% E5% a5% BD & content = this + Post + about + x-www-form-urlencoded

<form action="" method="post" target="" enctype="application/x-www-form-urlencoded">
  <p><label>Article title:<input type="text" name="title" value="" /></label></p>
  <p><label>Article content:<textarea name="content" rows="5" cols="33"></textarea></label></p>
  <p><button type="submit">Submit</button></p>
</form>

Note: due to historical reasons, the Url encoding implementation used by the form does not comply with the latest standards, and the spaces are treated as +.

// href: https://example.com/?title=%E4%BD%A0%E5%A5%BD&content=this%20post%20about%20x-www-form-urlencoded
new URL('https://example.com/?title = Hello & content = this post about x-www-form-urlencoded ')

When the data of MIME type is application/x-www-form-urlencoded, the definition in HTML and XForms specifications still adopts the earlier version, Replace spaces with "+" instead of "% 20". -- URL encoding the space character: + or %20?

In business, we usually do not send through action, but encapsulate through ajax/fetch. At this time, we need to encode or decode the data.

// title=%E4%BD%A0%E5%A5%BD&content=this%20post%20about%20x-www-form-urlencoded
params = `title=${encodeURIComponent('Hello')}&content=${encodeURIComponent('this post about x-www-form-urlencoded')}`

Note: the processing result of spaces encodeuricomponent ("") / /% 20

encodeURI: it cannot generate a URI suitable for HTTP GET or POST requests by itself. For ex amp le, for XMLHTTPRequests, because "&", "+", and "=" will not be encoded, but they are special characters in GET and POST requests

URLSearchParams

Encoding and decoding of relevant parameters can be completed through encodeURIComponent() and decodeURIComponent(), but the overall operation and processing are complex, especially in the process of obtaining specified parameters due to many parameters.

function enhanceUrlArgs(query){
	var args = {};
	query.replace(/([^?&=]+)=([^&]+)/g, function(full, key, value){
		args[key] = decodeURIComponent(value);
		return "";
	});
	return args;
}

// {title: "hello", content: "this post about x-www-form-urlencoded"}
enhanceUrlArgs(new URL('https://example.com/?title = Hello & content = this post about x-www-form-urlencoded ') search)

application/x-www-form-urlencoded data can be encoded and decoded through URLSearchParams, and the processing method is greatly simplified.

Example: simulate the above from expression submission form

const searchParams = new URLSearchParams()
searchParams.set('title', 'Hello')
searchParams.set('content', 'this post about x-www-form-urlencoded')

// title=%E4%BD%A0%E5%A5%BD&content=this+post+about+x-www-form-urlencoded
console.log(searchParams.toString()) 

Note: This is consistent with the default processing of the form!

Constructors can also accept an array of key / value pairs

new URLSearchParams([
  ['title', 'Hello'],
  ['content', 'this post about x-www-form-urlencoded']
])

Furthermore, it can also be "object"

new URLSearchParams({
  title: 'Hello',
  content: 'this post about x-www-form-urlencoded'
})

It can also be a "string"

new URLSearchParams('title=Hello&content=this post about x-www-form-urlencoded')	// location.search

Read mode

It corresponds to the setting method one by one

Example: get the above form data

for (const [key, value] of searchParams) {
  console.log(key, value)
}

Get "array"

// [['title ',' hello '], ['content', 'this post about x-www-form-urlencoded']]
[...searchParams] 

Get "object"

// {title: "hello", content: "this post about x-www-form-urlencoded"}
Object.fromEntries(searchParams) 

Object. The fromentries (Iterable) method converts the list of key value pairs into an object.

It should be noted that the key of the object is unique, and lossy conversion may occur

const searchParams2 = new URLSearchParams([
  ['category', 'javascript'],
  ['category', 'front end']
])

// "category=javascript&category=%E5%89%8D%E7%AB%AF"
searchParams2.toString()
// {category: "front end"}
Object.fromEntries(searchParams2)

The latter covers the former. When expressing from submission, select multiple is real and needs special attention.

Avoid lossy conversions:

Object.fromEntries(
	[...new Set(searchParams2.keys())].map(key => [key, searchParams2.getAll(key)])
)

Get specified data

methodexplain
searchParams.entries()Return a iterator Objects that can traverse all key / value pairs.
searchParams.get(key)Gets the first value of the specified search parameter
searchParams.getAll(key)Gets all the values of the specified search parameters and returns an array
searchParams.has(key)Determine whether this search parameter exists
searchParams.keys()Returns an iterator containing all key names of key / value pairs
searchParams.values()Returns an iterator that contains all the values of a key / value pair

Sample rewrite

const response = await fetch(url, {
  method: 'POST',
  body: new URLSearchParams({ text })
})
const json = await response.json()

If URLSearchParams is used as the body, the content type header will be automatically set to application/x-www-form-urlencoded.

FormData

What if the form contains files? application/x-www-form-urlencoded does not support files. It can be set to multipart / form data. If you need to send a request through ajax/fetch at this time, you can encapsulate the data with FormData.

The FormData interface provides a way to construct key/value pairs that represent form data, and can easily pass data through XMLHttpRequest.send() Method. Both this interface and this method are quite simple and direct. If the encoding type when sending is set to "multipart / form data", it will use the same format as the form.

Example: simulate the above from expression submission form

const formData = new FormData()
formData.set('title', 'Hello')
formData.set('content', 'this post about multipart-form-data')
formData.set('logo', document.forms[1].logo.files[0])	// document.forms[1].logo => fileInputElement

The constructor supports automatically including the form value in the form through the form element, including the file content, which will also be included after encoding.

new FormData(document.forms[0])

Read mode

Example: get the above form data

for (const [key, value] of formData) {
  console.log(key, value)
}

Other methods are not supported temporarily. The method of obtaining the specified data is similar to * * URLSearchParams * *, and the corresponding method is also provided. You can Self access.

Rewrite example

const formData = new FormData();
formData.set('text', text);

const response = await fetch(url, {
  method: 'POST',
  body: formData
})
const json = await response.json()

If FormData is used as the body, the content type header will be automatically set to multipart / form data.

Convert FormData to URLSearchParams

The form wants to be sent through application/x-www-form-urlencoded.

  1. Through the above example, add action directly to the form

  2. Convert

    const formElement = document.querySelector('form')
    const formData = new FormData(formElement)
    const searchParams = new URLSearchParams(formData)
    
    fetch(url, {
      method: 'POST',
      body: searchParams,
    })
    

    In this way, the file type will be lost.

Other types

Blobs

fetch(url, {
  method: 'POST',
  body: blob
})

The content type header is automatically set to Blob.type

Strings

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

Buffers

fetch(url, {
  method: 'POST',
  body: new Uint8Array([]),
  headers: { 'Content-Type': 'image/png' }
})

summary

If there are no files and query parameters, you can use * * URLSearchParams * *; If you include files, you need to use FormData.

Keywords: Javascript url form

Added by Mikkki on Thu, 30 Dec 2021 13:34:33 +0200