Упрощенный вопрос
почему при использовании express. js & request. js следующие два примера:
request.get(url)
.on('response' (requestjsResponse) => {
requestjsResponse.pipe(res);
})
и
request.get(url, (err, requestjsResponse, requestjsBody) => {
res.send(requestjsResponse)
})
Не дает одинаковых результатов, даже если requestjsBody
содержит ожидаемое содержимое?
Подробный вопрос
У меня есть две express.js
версии обработчика маршрута, которые обрабатывают некоторые процедуры проксирования файлов для нескольких типы файлов. Код использует стандартные обозначения express. js req/res/next
. В основном, что может быть важно из фона, информация, не связанная с кодом, для этой проблемы заключается в том, что два наиболее возвращаемых типа обрабатываются следующим образом:
- PDF: должны открываться в браузере, их размер обычно равен не менее 18 КБ (согласно заголовку длины содержимого)
- EML: загружается, размер их обычно меньше 16 КБ (согласно заголовку длины содержимого)
Оба обработчика версии используют request.js
, одну с
get(url: string, callback: (Error, Response, Body) => void)
формой, которую я буду называть формой обратного вызова , где внутри такого обратного вызова ожидается все тело . В этом случае ответ пользователю отправляется простым текстом express. js res.send(Body)
. другой использует форму
get(url: string).on(event: 'response', callback: listener: (request.Response) => void)
, которую я буду называть формой event / pipe , и передает ответ конечному пользователю, передавая его по request.Response.pipe(res)
внутри обработчика ответа. Подробности приведены в листинге кода.
Я не могу найти разницу между этими двумя формами, но: В случае .eml (MIME message / rfc822, вы можете угрожать им как причудливыми HTML) файлами версии работают точно так же, файл хорошо загружен.
В случае .pdf, при использовании формы события / канала get(url).on('response', callback)
Я могу успешно передать PDF-документ клиенту. Когда я использую форму обратного вызова (то есть get(url: string, callback: (Error, Response, Body) => void)
), даже когда я просматриваю тело в отладчике (кажется, что это полный PDF, содержит заголовок PDF, маркер EOF, например, c .t.), Клиент получает только какая-то странная преамбула, объявляющая HTML:
<!doctype html><html><body style='height: 100%; width: 100%; overflow: hidden; margin:0px; background-color: rgb(82, 86, 89);'><embed style='position:absolute; left: 0; top: 0;'width='100%' height='100%' src='about:blank' type='application/pdf' internalid='FD93AFE96F19F67BE0799686C52D978F'></embed></body></html>
, но впоследствии документ PDF не был получен. Chrome утверждает, что ему не удалось загрузить документ.
Пожалуйста, смотрите код:
Не работающая версия обратного вызова:
request.get(url, (err, documentResponse, documentBody) => {
if (err) {
logger.error('Document Fetch error:');
logger.error(err);
} else {
const documentResponseContentLength = Number.parseInt(documentResponse.headers['content-length'], 10);
if (documentResponseContentLength === 0 || Number.isNaN(documentResponseContentLength)) {
logger.warn('No content provided for requested document or length header malformed');
res.redirect(get404Navigation());
}
if (mimetype === 'application/pdf') {
logger.info(' overwriting Headers (PDF)');
res.set('content-type', 'application/pdf');
// eslint-disable-next-line max-len, prefer-template
res.set('content-disposition', 'inline; filename="someName.pdf"');
logger.info('Document Download Headers (overridden):', res.headers);
}
if (mimetype === 'message/rfc822') {
logger.info(' overwriting Headers (message/rfc822)');
res.set('content-type', 'message/rfc822');
// eslint-disable-next-line max-len, prefer-template
res.set('content-disposition', 'attachment; filename="someName.eml"');
logger.info('Document Download Headers (overridden):', res.headers);
}
res.send(documentBody) /* Sending message to clinet */
}
})
.on('data', (d) => {
console.log('We are debugging here')
})
Рабочее событие на основе / передано версия:
const r = request
.get(url)
.on('response', (documentsResponse) => {
if (Number.parseInt(documentsResponse.headers['content-length'], 10) !== 0) {
// Überschreibe headers für PDF und TIFF, diese kommen gelegentlich unvollständig an
if (mimetype === 'application/pdf') {
logger.info(' overwriting Headers (PDF)');
res.set('content-type', 'application/pdf');
res.set('content-disposition', 'inline; filename="someName".pdf"')
logger.info('Document Download Headers (overridden):', documentsResponse.headers);
}
if (mimetype === 'message/rfc822') {
logger.info(' overwriting Headers (message/rfc822)');
res.set('content-type', 'message/rfc822');
res.set('content-disposition', 'attachment; filename="someName".eml"');
logger.info('Document Download Headers (overridden):', res.headers);
}
r.pipe(res); /* Response is piped to client */
} else {
res.redirect(get404Navigation());
}
}
.on('data', (d) => {
console.log('We are debugging here')
})
Событие, в котором часть с r.pipe(res)
кажется очень подозрительным (см., где объявлено r
и где используется), это версии, которые работают правильно для обоих случаев.
Я предполагаю, что эта проблема может быть вызвана природой отправки многокомпонентного контента, поэтому я добавил дополнительные on('data', (d)=>{})
обратные вызовы и установил точки прерывания, чтобы видеть, когда завершается / передается ответ, когда вызывается обработчик данных, и результаты соответствуют моим ожиданиям :
request(url, (err, response, body))
случай, обработчик данных вызывается дважды, перед выполнением обратного вызова, все тело доступно внутри обработчика, поэтому мне еще более неясно, что я не могу просто res.send
его. request.get(url).on('response')
Сначала вызывается piping to res, затем вызывается обработчик данных два раза. Я полагаю, что внутренние внутренности механизма HTTP node.js выполняют асинхронный трюк и выдвигают ответы один за другим при получении каждого чанка ответа.
Я буду рад любому объяснению, что я делаю неправильно и что я могу выровнять, чтобы заставить мою версию обратного вызова работать как положено для случая PDF.
Эпилог: Почему такой код используется? Наш бэкэнд извлекает данные PDF с внешнего, не подверженного воздействию publi c inte rnet сервера, но из-за устаревших причин некоторые заголовки установлены неправильно (в основном Content-Disposition
), поэтому мы перехватываем их и действуем как своего рода прокси выравнивания между источником данных и клиентом.