使用节点 https get 请求强制纯文本编码

Force plain text encoding with node https get request

问题

相关 - 但这个答案对我不起作用。

我正在尝试调用 Stack Overflow API questions 端点:

https://api.stackexchange.com/2.3/questions?site=Whosebug&filter=total

哪个应该 return 以下 JSON 响应:

{"total":21951385}

示例代码

我正在使用这样的 https node module to submit a get 请求:

const getRequest = (url: string) => new Promise((resolve, reject) => {
    const options: RequestOptions = {
        headers: {
            'Accept': 'text/*',
            'Accept-Encoding':'identity',
            'Accept-Charset' : 'utf8',
        }
    }

    const req = get(url, options, (res) => {
        res.setEncoding('utf8');
        let responseBody = '';
        res.on('data', (chunk) => responseBody += chunk);
        res.on('end', () => resolve(responseBody));
    });

    req.on('error', (err) => reject(err));
    req.end();
})

然后像这样调用它:

const questionsUrl = 'https://api.stackexchange.com/2.3/questions?&site=Whosebug&filter=total'
const resp = await getRequest(questionsUrl)
console.log(resp)

但是,我得到了回复:

▼�
�V*�/I�Q�22�454�0�♣��♥‼���↕

我尝试过的

我试过做以下几种变体:

此代码也适用于几乎任何其他 API 服务器,例如使用以下 url:

https://jsonplaceholder.typicode.com/todos/1

但是 StackOverlow API 在我尝试过的任何其他地方都可以使用,因此必须有一种方法来指示节点如何执行它。

响应头 - 内容编码 - Gzip

正如 指出的那样 - 看起来服务器不尊重正在传递的 Accept-Encoding 值并返回压缩响应 none-the-less.

解压缩响应

根据How do I ungzip (decompress) a NodeJS request's module gzip response body?上的回答,可以这样解压:

import { get } from 'https'
import { createGunzip } from 'zlib'

const getRequest = (url: string) => new Promise((resolve, reject) => {
    const req = get(url, (res) => {
        const buffer: string[] = [];

        if (!res.headers['content-encoding']?.includes('gzip')) {
            console.log('utf8')
            res.on('data', (chunk) => buffer.push(chunk));
            res.on('end', () => resolve(buffer.join("")))
        } else {
            console.log('gzip')
            const gunzip = createGunzip();
            res.pipe(gunzip);
            gunzip.on('data', (data) => buffer.push(data.toString()))
            gunzip.on("end", () => resolve(buffer.join("")))
            gunzip.on("error", (e) => reject(e))
        }
    });

    req.on('error', (err) => reject(err));
    req.end();
})

我的建议是使用内置支持 promises 和 gzip 的 http 库。我目前最喜欢的是 got()http.get() 就像任何地方功能最少的 http 请求库。你真的不必自己写所有这些。这是使用 got() 库的整个代码的样子:

const got = require('got');

function getRequest(url) {
    return got(url).json();
}

这个库会自动处理你需要的所有这些事情:

  1. 承诺
  2. JSON 转换
  3. Gzip解码
  4. 2xx 状态检测(其他状态代码如 404 会变成您的代码不会执行的承诺拒绝)。

而且,它还有许多其他一般用途的有用功能。使用 http.get() 手动编码的日子应该早已结束。无需重写已经为您编写并经过良好测试的代码。

仅供参考,这里有一个非常强大的 http 库列表:https://github.com/request/request/issues/3143。您可以选择您最喜欢的API。