preface
Using fetch/axios often involves file uploads, as well as other requests, including some content-Types that get overwhelmed by these different types. This article will sort out when and which type to use.
Example analysis, how to upload a picture
Form submission
The form <form> is used to collect user-submitted data and send it to the server. The following code contains: file selection box (get local file), submit button (submit form control).
<form action="http://localhost:8899/react/aa" method="post" enctype="multipart/form-data"> <input type="file" Name ="image" multiple="multiple" /> <input type="submit" /> </form>Copy the code
When the user clicks the “Submit” button, each control generates a key-value pair. The key name is the name property of the control, and the key-value is the value property of the control. We use Node as the server and use koa-body to parse the file passed by post
Router.post ('/aa', async CTX => {console.log(ctx.request.files); })Copy the code
Use axios to request
Form data is sent to the server as key-value pairs, which the browser does automatically. Sometimes, however, we want to script through the process of constructing and editing form key-value pairs. Browsers natively provide FormData objects to do this.
new FormData(form)
let formdata = new FormData(form);
Copy the code
The FormData() constructor takes an optional argument to a form element. If the parameter is omitted, an empty form is represented, otherwise key-value pairs within the form elements are processed. The usual approach is to build an empty form object. FormData provides many instance methods, and we can add key-value pairs to the form through the Append method.
formdata.append(key1,value1)
formdata.append(key2,value2)
Copy the code
The following code implements file upload by submitting the formdata formdata through axios
<input type="file" @change="onChange">
methods:{
onChange(event){
const params = new FormData()
params.append('file',event.target.files[0])
axios.post('http://localhost:8899/react/aa',params,{
headers:{
'content-type':'multipart/form-data'
}
})
}
}
Copy the code
Content-type: lib/adapters/xhr.js defines the browser to use XHR:
module.exports = function xhrAdapter(config) { return new Promise(function dispatchXhrRequest(resolve, {{url,method,data,headers}} var requestData = config.data; var requestHeaders = config.headers; // Check whether it is an instance of formData, If (utils.isformData (requestData)) {delete requestHeaders[' content-type ']; // Let the browser set it } }) }Copy the code
IsFormData implementation: FormData is the constructor of the form object, and instanceof is used to check whether the constructor’s prototype property appears on the prototype chain of the instance object.
/** * Determine if a value is a FormData * * @param {Object} val The value to test * @returns {boolean} True if value is an FormData, otherwise false */ function isFormData(val) { return (typeof FormData ! == 'undefined') && (val instanceof FormData); }Copy the code
Set the default value for Content-Type
Lib/defaults. Js file
defaults.headers = {
common: {
'Accept': 'application/json, text/plain, */*'
}
};
utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
defaults.headers[method] = {};
});
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
});
Copy the code
Determine the type of incoming data to set invalid content-Type
When a request is made using AXIos, the default transformRequest is set to change the requested data before it is sent to the server. ‘PUT’, ‘POST’, ‘PATCH’ and ‘DELETE’ are only valid for these requests.
// lib/defaults.js file transformRequest: [function transformRequest(data, headers) { normalizeHeaderName(headers, 'Accept'); normalizeHeaderName(headers, 'Content-Type'); if (utils.isFormData(data) || utils.isArrayBuffer(data) || utils.isBuffer(data) || utils.isStream(data) || utils.isFile(data) || utils.isBlob(data) ) { return data; } if (utils.isArrayBufferView(data)) { return data.buffer; } if (utils.isURLSearchParams(data)) { setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded; charset=utf-8'); return data.toString(); } if (utils.isObject(data)) { setContentTypeIfUnset(headers, 'application/json; charset=utf-8'); return JSON.stringify(data); } return data; }],Copy the code
Generally, content-type is not required when using axios requests. If there are special cases, you can process the content-type in transformRequest:[function(data,headers){return data}]
Using fetch requests
To illustrate, we can directly simplify the encapsulation of fetch requests. For details, see the following codepen example. This is mainly to show what happens when fetch is used to upload a file and headers request is set at the same time.
fetch(api,{
url,
headers:{
'content-type':'multipart/form-data'
},
mode:'no-cors'
})
Copy the code
When we initiated the image upload request, we got two error messages. Click on the file where the error occurred to the right of the error message
500 (Internal Server Error)
The request API server reported an error,Unexpected end of input
An error was reported when the browser ran Response.json ().Unexpected end of Input why do browsers declare Unexpected end of inputI’ll talk about it separately later, but let’s look at why the server reported an error.
The Content of the uploaded file needs to be marked by boundary. The correct content-type: mutlipart/form-data; Boundary = —– XXXX. View our current upload request:
Compare the content-Type format for the correct image upload:
Setting the Content-Type will lose the boundary parameter when using the FETCH request, soWhen uploading a file
, there is no need to set the headers field, the browser will automatically generate the full Content-Type (including boundary).
After removing the headers field, the FETCH API can upload files normally!!
Codepen sample
Codepen sample
Unexpected end of input error
The fetch API is used to fetch a resource and return a response object. If we specify content-type as application/json then the server will return us a jSON-formatted string, so when we call resopNse.json () we can parse out the correct object.
fetch(api).then(response=>response.json()).then(res=>res)
Copy the code
Parse () = json.parse () = json.parse () = json.parse () = json.parse () = Unexpected end of input The following code will output this error, causing the browser to read without a word.
JSON.parse("{")
JSON.parse('[{"test": 4}')
Copy the code
Unexpected Token < in JSON at position 0 is common, and the front end continues to use Response. JSON to parse the data returned by the server. If the content-type is not returned on the server, you will get this error.
/ / the server router. Post ('/aa, async CTX = > {CTX. Body = '< div > content < / div >'})Copy the code
Parse (“
1
“) will get the same result if you want a simple simulation using json.parse (“
1
“). The following code is the same.
JSON.parse("{sd}")
// Unexpected token s in JSON at position 1
JSON.parse("d}")
// Unexpected token d in JSON at position 0
Copy the code
Json.parse () supports the following types:
JSON.parse('{}'); // {} JSON.parse('true'); // true JSON.parse('"foo"'); // "foo" JSON.parse('[1, 5, "false"]'); // [1, 5, "false"] JSON.parse('null'); // null JSON.parse('1'); / / 1Copy the code
Therefore, in order to correctly parse the return value of the server, the content-type corresponding data type should be set uniformly at the front and back ends. The response object also supports other methods
Organize the content-type
axios
Get request mode
The request mode for GET will only use application/ X-www-form-urlencoded
Axios. Get ('/user '{params: {id: 1, name:' dd 'person:' Joe '}})Copy the code
In theory will request to http://localhost:8080/user? Id =1&name= DD&person = Zhang SAN but Url encoding will be done for special characters. Url Encoding is often called Url Encoding (also known as percent-encoding) because it is so simple, Use the % percent sign followed by a two-digit character — 0123456789ABCDEF — to represent a single byte in hexadecimal form. The default character set used for Url encoding is US-ASCII. For example, a corresponding number of bytes in the US – ASCII is 0 x61, then get after Url encoding is % 61 so after encoding the actual request path into http://localhost:8080/user? id=1&name=dd&person=%E5%BC%A0%E4%B8%89
Post request mode
- Application/x – WWW – form – urlencoded format
This mode is used by default. When using this mode, you need to process the incoming parameters intoname=hehe&age=10
If you pass the pair as an object, the default configuration in AXIos will set the content-type to application/json; charset=utf-8
const service = axios.create({ baseURL, Headers}) {application/x-www-form-urlencoded' content-type if (isFormUrlencoded) {let list: application/x-www-form-urlencoded' content-type if (isFormUrlencoded) { string[] = [] for (let key in params) { list.push(`${key}=${encodeURIComponent(params[key])}`) } fetchResult = service({ Url, method: 'POST', // process {a:1,b=2} into a=1&b=2 and pass it to data: list.join('&') }) } else { fetchResult = service.post(url, params) }Copy the code
- text/plain
Axios. Post (' http://localhost:8899/react/aa ', 'I am content, {headers: {' the content-type:' text/plain '}})Copy the code
Multipart /form-data upload file type does not need to be set, the browser will automatically add!!
4. application/json axios.post('http://localhost:8899/react/aa',{name:'dd',age:18})
fetch
Get application/ X-www-form-urlencoded
The method of get passing parameters needs to be added to the path, so the WORK of Url encoding needs to be implemented manually
Fetch ('http://localhost:8080? a=1&b=2', { method: For (let key in params){param += '${key}=${encodeURIComponent(params[key])}&'}Copy the code
Post way
- application/x-www-form-urlencoded
fetch('http://localhost:8080',{
method:'POST',
headers:{
'content-type':'application/x-www-form-urlencoded'
},
mode:'no-cors',
body:'name=dd&age=12'
})
Copy the code
- application/json
You need to use json.stringify () to convert the object into a JSON string, with the body as the field to accept the data
fetch(url, {
method: 'POST', // or 'PUT'
body: JSON.stringify(data),
headers: new Headers({
'Content-Type': 'application/json'
})
})
Copy the code
- Multipart /form-data upload files do not need to set this type, the browser will automatically add!!
let data = new FormData()
data.append('file',target.files[0])
fetch(url,{
body:data,
method:"POST",
headers:{}
})
Copy the code
reference
Error when POST file multipart/form-data Chrome: Uncaught SyntaxError: Unexpected End of Input URL Encoding and decoding Request header Screenshot