background
In the process of developing wechat mini programs, it is necessary to connect with the content audit interface of wechat, that is, upload a picture to check whether it is illegal or not. An AXIos upload returns 412; a Request upload returns 412.
However, if the interface of wechat is replaced by its own, the data can be successfully received, so there must be some differences between axios and Request when sending HTTP requests. So how do you find the difference? The only way to find the difference is to catch the bag.
Caught screening
First open Charles, then run the axios and Request code respectively. Note that to configure the proxy in the code, let’s look at the request code:
var request = require('request')
var fs = require('fs')
var options = {
method: 'POST'.url:
'https://api.weixin.qq.com/wxa/img_sec_check?access_token=xxx'.headers: {
'Content-Type': 'application/json',},formData: {
contentType: 'image/jpeg'.value: fs.createReadStream('/Users/keliq/Pictures/jzm.jpeg'),},proxy: 'http://127.0.0.1:8888'.rejectUnauthorized: false,
}
request(options, function (error, response) {
if (error) throw new Error(error)
console.log(response.body)
})
Copy the code
Packet capture results are as follows:
-
request
POST /wxa/img_sec_check? access_token=xxx HTTP/1.1 Content-Type: multipart/form-data; boundary=--------------------------630489123810205833486663 host: api.weixin.qq.com content-length: 11998 Connection: close ----------------------------630489123810205833486663 Content-Disposition: form-data; name="contentType" image/jpeg ----------------------------630489123810205833486663 Content-Disposition: form-data; name="value"; filename="jzm.jpeg"The content-type: image/jpeg y Ø ya...Copy the code
-
The response
HTTP/1.1 200 OK Date: Mon, 05 Apr 2021 06:59:26 GMT Content-Type: Application /json; HTTP/1.1 200 OK Date: Mon, 05 Apr 2021 06:59:26 GMT Content-Type: Application /json; encoding=utf-8 RetKey: 11 LogicRet: 42001 Content-Length: 81 Connection: close {"errcode":42001,"errmsg":"access_token expired rid: 606ab54e-4b90bc8a-66e6fc25"}Copy the code
Let’s look at the axios code
var axios = require('axios')
var FormData = require('form-data')
var fs = require('fs')
var data = new FormData()
data.append('contentType'.'image/jpeg')
data.append('value', fs.createReadStream('/Users/keliq/Pictures/jzm.jpeg'))
var config = {
method: 'post'.url:
'https://api.weixin.qq.com/wxa/img_sec_check?access_token=xxxx'.headers: {
'Content-Type': 'application/json'. data.getHeaders(), },data: data,
proxy: {
host: '127.0.0.1'.port: 8888,
},
}
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data))
})
.catch(function (error) {
console.log(error)
})
Copy the code
Packet capture results are as follows:
-
request
POST /wxa/img_sec_check? Accept: application/json, text/plain, */* Content-type: multipart/form-data; A boundary = -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - 571426341398317845770943 the user-agent: axios / 0.21.1 host: api.weixin.qq.com Transfer-Encoding: chunked Connection: close ----------------------------571426341398317845770943 Content-Disposition: form-data; name="contentType" image/jpeg ----------------------------571426341398317845770943 Content-Disposition: form-data; name="value"; Filename = "JZM. Jpeg" content-type: image/jpeg y Ø ya...Copy the code
-
The response
HTTP/1.1 412 Feed Additive Failed Date: Mon, 05-APR-2021 07:00:41 GMT Content-Length: 0 Connection: closeCopy the code
After comparison, axios has one more transfer-encoding header with a value of chunked than Request, while Request has one more Content-Length header than Axios. Therefore, the difference between these two headers is the primary culprit that causes the wechat interface to return 412.
Learn about Content-Length and Transfer-Encoding headers
I have checked a lot of information on the Internet, and found that piao Ruiqing’s article is very detailed, which is excerpted as follows:
- Content-length: indicates the Length of an HTTP message. It is the number of eight-bit bytes expressed in decimal digits
- Transfer-encoding: If the message length cannot be obtained before the request processing is complete, use transfer-encoding: chunked
How content-Length works
Content-length should be exact, otherwise it will cause an exception. This size includes all Content encoding. For example, if a text file is gzip compressed, the content-Length header refers to the compressed size, not the original size. What if the numbers provided are inaccurate?
- Content-length > Actual Length: After reading the end of the message, the server or client waits for the next byte and does not respond until timeout.
- Content-length < Actual Length: The first time the message is truncated and the second time it is parsed incorrectly.
How transfer-Encoding works
The data is sent as a series of blocks, and at the beginning of each block the length of the current block is added, in hexadecimal form, followed by \r\n, followed by the block itself, and also \r\n. The terminating block is a regular block, the difference being that its length is 0, as shown below:
Result after packet capture:
The solution
The first thing to do is to kill the Transfer-Encoding header in the Axios interceptor and add the Content-Length header:
axios.interceptors.request.use(
config= > {
console.log('config', config.headers)
delete config.headers['Transfer-Encoding']
return config
},
error= > Promise.reject(error)
)
Copy the code
It turns out that the content-Length and Transfer-Encoding headers do not coexist, which means that if I manually add the Content-Length header, Automatically kills transfer-encoding headers…
So how do you get the content-Length? Finally, I found the same problem in the CNodeJS community. I dug into the source code of Request, Axios, and form-data, and finally found a solution, which uses the getLength method of form-data to get the length. Then add the Content-Length header manually.
The code after the final transformation is as follows:
var axios = require('axios')
var FormData = require('form-data')
var fs = require('fs')
async function request() {
var data = new FormData()
data.append('contentType'.'image/jpeg')
data.append('value', fs.createReadStream('/Users/keliq/Pictures/jzm.jpeg'))
var len = await new Promise((resolve, reject) = > {
return data.getLength((err, length) = > (err ? reject(err) : resolve(length)))
})
var config = {
method: 'post'.url: 'https://api.weixin.qq.com/wxa/img_sec_check?access_token=xxx'.headers: {
'Content-Type': 'application/json'. data.getHeaders(),'Content-Length': len,
},
data: data,
proxy: {
host: '127.0.0.1'.port: 8888,
},
}
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data))
})
.catch(function (error) {
console.log('error')
})
}
request()
Copy the code