获取到的request中Content-Length与实际不符的Nodejs疑问
获取到的request中Content-Length与实际不符的Nodejs疑问
有一个静态网页,包含一个表单,表单中有一个文件域,nodejs这边是一个简单的http服务器,在静态网页那边提交了一个文件,在nodejs这边用request.on(“data”,function(chunk){})获取请求正文的长度,代码如下
var _post_data = "";
request.on("data", function (chunk) {
_post_data += chunk;
console.log("_post_data长度:" + _post_data.length);
})
当整个获取结束之后,查看_post_data的长度与请求头中的content-length长度不符合,要少一点。。。请问这是为啥,我如何才能完整的获取到请求正文,因为想把这个请求正文,原样转发给另外一个地址(用http.request做)
看官方文档,chunk是经过解码后的字符串了,不知道是不是这个原因。。。那如何获得解码前得字符串呢,或者说不让请求正文解码,直接获取到。谢谢各位啦
您的问题涉及到了 Node.js 中处理 HTTP 请求时遇到的 Content-Length
不符的问题。这种情况通常发生在您尝试手动累积请求体数据时,可能会出现部分数据未到达就提前结束了累积过程。
原因分析
- 事件流处理:
request.on("data", ...)
是一个事件处理器,用于处理接收到的数据块。这些数据块可能不会一次性全部到达,而是分多次到达。因此,如果您在所有数据到达之前就开始处理_post_data
,可能会导致数据不完整。 - 内存管理:Node.js 的事件循环机制可能导致某些数据块未能及时触发
data
事件,尤其是在高负载或网络延迟较大的情况下。
解决方案
为了确保能够完整地接收并处理请求体,您可以使用 request.on("end", ...)
来确保所有数据已经完全接收后再进行处理。此外,您可以使用 Buffer
对象来存储数据块,以避免字符串拼接带来的性能问题和潜在的编码问题。
const http = require('http');
const querystring = require('querystring');
http.createServer((req, res) => {
if (req.method === 'POST') {
let _post_data = Buffer.alloc(0); // 使用 Buffer 存储数据
req.on('data', (chunk) => {
_post_data = Buffer.concat([_post_data, chunk]);
console.log(`当前数据长度: ${_post_data.length}`);
});
req.on('end', () => {
console.log(`最终数据长度: ${_post_data.length}`);
// 您可以在这里处理 _post_data
// 例如,将请求体转发到另一个地址
const options = {
hostname: 'example.com',
port: 80,
path: '/upload',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': _post_data.length
}
};
const post_req = http.request(options, (res) => {
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`响应数据: ${chunk}`);
});
});
post_req.write(_post_data.toString());
post_req.end();
});
}
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(3000);
关键点解释
- 使用
Buffer
: 使用Buffer
而不是字符串来累积数据,这样可以避免由于字符串拼接造成的性能问题,并且可以更好地处理二进制数据。 req.on('end', ...)
: 确保在所有数据到达后才处理数据,这样可以确保_post_data
包含了完整的请求体。- 转发请求: 在
req.on('end', ...)
中,您可以使用http.request
将接收到的数据转发到其他服务。
通过这种方式,您可以确保完整地接收并处理请求体,从而避免 Content-Length
不符的问题。
改了之后,好像大小还是不对。。。请求正文的长度又比Content-Length要长了好多,囧。。。。你知道如何把request获取到的请求体直接当做另外一个request的请求体发出去么,相当于copy了一份,如果用data监听器获取的话,是经过转码的,因为里面有文件的上传,所以请求正文会有二进制数据,不知道经过转码之后是不是就不对了。。。
嗯,搞定啦~直接获取请求主体,然后再次请求,不过现在有一个非常郁闷的问题,就是用http.request请求一个地址,方法是get,其实就是一个302的Redirect,但是请求返回的结果依然是另外一个Redirect 我又把地址直接复制到浏览器里面,是可以正常返回的,状态码是200,用xmlhttprequest也是返回成功,不知道为什么http.request就不对,按道理来说不应该呀
用下面这段代码访问上面的地址,返回302,但是在浏览器里面直接访问上面的地址,看控制台应该直接输出200.。。。
http.get({
host:“www.google.com.hk”, port:80, path:"/search?tbs=sbi:AMhZZivK0ZITunLa61jpSnTUgxTzXokI3SvM61VJdh8_1jtioUzo-LZMimgb-a3eh0eu6CtIrSOMldhyYsbBsK3H_1PaFE1c6LA4FeeeqH3ZLSO6ocA9tUmVU-XD2IsWQL91FcogEj85LxW_1D9JD62lt6jYMRYyXJAFgws_1BIuWBztJ27pNSGd-2YHGKVPQCOMubdQ9Cs86bYBsKaA1FsyM7MQDqr4WZ9sNeH_1ZLUcbDUTuWTQ25khYsCYmpX2XYDeMGtj_1xwZtlrrMDRINTYDKlCfAKy9CgqYClVWZk9xJSRNDcsyfbUk9rJIY2whiJ70B4e3J0ohq3H5wDeMPRXmM1Lh3AmMZ1sNDXuUs_1Nyey0pqYe41V-aDZzVRObYkvyakU6lUFLegybs4sQxKE8nx8OkzqqOCGqZKQGzysmkWY0XNpfMOMFGPGs-pZqU8P1kFxM1dWJSA-fpNhbxaacdZadbsb98imQVRKI-WGPlEO8y2uaFBbSvF_1L0yQciRzJpXhABB7YN8elIAnSpJRx2Xx3HKzFYqRV4bFXe-8h1ppqUblRlrjCLuH2WNPMOroFb6XEkiE0CbHFIvGnucO4nq4T3vkYUrNWC034ziV5IDfacaGqU6b4ejqSBe_1-2gz-GOdaUrtygNzYtWYlVpWl0XUXcvJuuP3hUHqs1Aqs99I5WUBclqi82YHQRytlIQo_1G16YbDiw1Z9rkJliiPX3iofsMWcWbLsXrK2FBFK4vRj1Wh_1bRlYdCO9P0Hvn6MBCtkcuQCfPKe5HcSvowpofKFlA-Dvm3E7NYVIgPpTmYeH43DyAa9-plERGVchoC_1U1IEPaBIa5qQyqKBez0232oT1A3Ijuh0FjrYYm336YwspyITrdFgFtfDrqe7jUHBC3VbdXZR1pwYm-5A3APIfbNskqiNXJ2x2F57GjFncUiTwqECyZomj4bxFPT0IbGFmzVv97LbTTdIEItAQ2ABHRNGsFG9hKy38iuSUq3DMk5_1ONm7MC4dmEypy9lun1KQ5rf18Z0RyCjs3P76StUl5MnzHBZKUJ_1gWg18c5djMvxWWURwcObDrmgaC7Mssb0CN0CRxLtW0LAzJzxpC9V4-AX3Nc_11xMt45Ui6Mmb2N0k4oIOqGYKSMcTjUK-RoiIgPw00OgNc6qBr-i6h74ArH7iD5LLSq_1f8C0aDN7Y0M0pkEnkbFtF81u7VRQs9agF8LWNv6MnlvHQEzMXNjMtcC1eTTcGiTpiUnuVtXb1hsSFnCjoO9G4D5gHfn7smJaz50Qxq3CrdQLKfpWZ8Y-BukuvbVdVR2_1vg1_1kAlU3-5owOds16WQlTsHHmIb09B8E7PwIqgJfv1y8RtiLR3zhGPxRd4TEc32pHBzdRw" },function(res){ console.log(res.statusCode); });
试试加一些headers再请求,比如 user-agent, accpet(可以照抄你浏览器的),可能是服务器端对客户端信息进行判断了呢
看看一下代码:第一次请求返回302,然后按照给出的url再请求就200了,参考一下吧
var http = require('http');
var url = require('url');
http.get({
host:“www.google.com.hk”,
port:80,
path:"/search?tbs=sbi:AMhZZivK0ZITunLa61jpSnTUgxTzXokI3SvM61VJdh8_1jtioUzo-LZMimgb-a3eh0eu6CtIrSOMldhyYsbBsK3H_1PaFE1c6LA4FeeeqH3ZLSO6ocA9tUmVU-XD2IsWQL91FcogEj85LxW_1D9JD62lt6jYMRYyXJAFgws_1BIuWBztJ27pNSGd-2YHGKVPQCOMubdQ9Cs86bYBsKaA1FsyM7MQDqr4WZ9sNeH_1ZLUcbDUTuWTQ25khYsCYmpX2XYDeMGtj_1xwZtlrrMDRINTYDKlCfAKy9CgqYClVWZk9xJSRNDcsyfbUk9rJIY2whiJ70B4e3J0ohq3H5wDeMPRXmM1Lh3AmMZ1sNDXuUs_1Nyey0pqYe41V-aDZzVRObYkvyakU6lUFLegybs4sQxKE8nx8OkzqqOCGqZKQGzysmkWY0XNpfMOMFGPGs-pZqU8P1kFxM1dWJSA-fpNhbxaacdZadbsb98imQVRKI-WGPlEO8y2uaFBbSvF_1L0yQciRzJpXhABB7YN8elIAnSpJRx2Xx3HKzFYqRV4bFXe-8h1ppqUblRlrjCLuH2WNPMOroFb6XEkiE0CbHFIvGnucO4nq4T3vkYUrNWC034ziV5IDfacaGqU6b4ejqSBe_1-2gz-GOdaUrtygNzYtWYlVpWl0XUXcvJuuP3hUHqs1Aqs99I5WUBclqi82YHQRytlIQo_1G16YbDiw1Z9rkJliiPX3iofsMWcWbLsXrK2FBFK4vRj1Wh_1bRlYdCO9P0Hvn6MBCtkcuQCfPKe5HcSvowpofKFlA-Dvm3E7NYVIgPpTmYeH43DyAa9-plERGVchoC_1U1IEPaBIa5qQyqKBez0232oT1A3Ijuh0FjrYYm336YwspyITrdFgFtfDrqe7jUHBC3VbdXZR1pwYm-5A3APIfbNskqiNXJ2x2F57GjFncUiTwqECyZomj4bxFPT0IbGFmzVv97LbTTdIEItAQ2ABHRNGsFG9hKy38iuSUq3DMk5_1ONm7MC4dmEypy9lun1KQ5rf18Z0RyCjs3P76StUl5MnzHBZKUJ_1gWg18c5djMvxWWURwcObDrmgaC7Mssb0CN0CRxLtW0LAzJzxpC9V4-AX3Nc_11xMt45Ui6Mmb2N0k4oIOqGYKSMcTjUK-RoiIgPw00OgNc6qBr-i6h74ArH7iD5LLSq_1f8C0aDN7Y0M0pkEnkbFtF81u7VRQs9agF8LWNv6MnlvHQEzMXNjMtcC1eTTcGiTpiUnuVtXb1hsSFnCjoO9G4D5gHfn7smJaz50Qxq3CrdQLKfpWZ8Y-BukuvbVdVR2_1vg1_1kAlU3-5owOds16WQlTsHHmIb09B8E7PwIqgJfv1y8RtiLR3zhGPxRd4TEc32pHBzdRw"
}, function (res) {
console.log(res.statusCode);
var direct = url.parse(res.headers.location);
http.get(direct, function (res) {
console.log(res.statusCode);
});
});
另外,服务器端还有返回cookie的,如果你在下次请求时没带上这个信息的话,某些情况下也会导致一直302的。至于为什么浏览器访问就返回200了,那是因为很多工作浏览器都帮你处理了,而你自己用Node.js来模拟这个请求时,需要熟悉http协议的工作原理才行
搞定啦,多谢~
请问http.get方法模拟访问一个页面,需要带2个cookie,该如何写啊?
在处理HTTP请求时,Content-Length
不匹配的问题通常是因为数据接收过程中出现了一些问题,例如数据未完全接收或处理不当。在Node.js中,你可以通过正确处理data
事件来确保完整地接收到请求体。
以下是改进后的示例代码:
const http = require('http');
const server = http.createServer((req, res) => {
let postData = '';
req.on('data', (chunk) => {
// 将每个chunk拼接到一起
postData += chunk;
});
req.on('end', () => {
// 在这里可以使用完整的postData
console.log('postData长度:', Buffer.byteLength(postData, 'binary'));
console.log('Content-Length:', req.headers['content-length']);
// 转发请求正文到其他地址
const options = {
hostname: 'example.com',
port: 80,
path: '/path',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData, 'binary')
}
};
const postRequest = http.request(options, (response) => {
response.setEncoding('utf8');
response.on('data', (chunk) => {
console.log('Response:', chunk);
});
});
postRequest.write(postData);
postRequest.end();
res.end('Request processed');
});
});
server.listen(3000, () => {
console.log('Server is listening on port 3000');
});
解释
- 数据收集:在
data
事件中,我们通过累加chunk
来构建完整的请求体。 - 数据结束:
end
事件表示请求体已完全接收,此时我们可以进行后续操作,如转发请求。 - 转发表单数据:在转发请求时,需要设置正确的
Content-Length
,并使用Buffer.byteLength
计算二进制长度,以确保正确传递请求体大小。
这样做可以确保你完整地接收和处理请求体,并且能够准确地转发请求到其他地址。