问题
在对接第三方接口时接口返回的响应数据格式为GBK
而服务端语言使用的是nodejs
于是常规的方式接收到数据后中文呈现乱码
心路历程
通过网上查找解决方案,最多的就是使用以下方式将GBK
转为UTF
// 可以通过该命令`npm install iconv-lite`安装插件,并引入(*已安装可忽略*)
const iconv = require("iconv-lite")
let str = "你好"
let gbkStr = iconv.encode(str, "gbk") // 可以通过encode将字符串转为gbk格式的二进制buffer流
console.log(str,"转为GBK后", gbkStr)
console.log("GBK字符串显示", gbkStr.toString())
let utfStr = iconv.decode(gbkStr, 'gbk') // 然后将二进制buffer流通过decode转换回来
console.log("复原字符串显示", utfStr)
// output
// 你好 转为GBK后 <Buffer c4 e3 ba c3>
// GBK字符串显示 ���
// 复原字符串显示 你好
这个方案很简单但是我遇到了另一个简单的问题
axios.post(TAPI_URL, {
sign: sign,
agentAccount: TAPI_ACCOUNT,
busiBody: data,
}).then((res) => {
console.log("res.data: ", res.data)
})
// 通过打印可以看出我们在http响应中拿到的数据已经是字符串了
// output
// res.data: {
// "code": 0,
// "msg": "���"
// }
那么怎么通过�����ɹ�
已经乱码的字符串转换成buffer
再解析呢,我又查阅了很多网上的方案还是同上面一样,只要搜索nodejs
中gbk
转utf
出现的就都是说用iconv.decode(buffer, 'gbk')
怎么样都没找到将已经乱码的字符串转换成buffer
解决问题
于是我转而思考另一个方向,可以在接收响应的时候就直接接收二进制buffer流吗?
果然换了一个方向得以解决了这个问题
axios.post(TAPI_URL, {
sign: sign,
agentAccount: TAPI_ACCOUNT,
busiBody: data,
}, {
responseType: 'arraybuffer' // 设置响应数据类型为二进制缓存流
}).then((res) => {
console.log("res.data: ", res.data)
// 别忘了将转化好后的字符串恢复为json对象
const data = JSON.parse(iconv.decode(res.data, "gbk")) // 解析gbk字符集
console.log("data: ", data)
})
// output
// res.data: {
// "code": 0,
// "msg": "���"
// }
// data: {
// "code": 0,
// "msg": "你好"
// }
思考
通过以上的问题我们发现了两个关键点
buffer
responseType
buffer
iconv
源码
declare module 'iconv-lite' {
export function decode(buffer: Buffer, encoding: string, options?: Options): string;
export function encode(content: string, encoding: string, options?: Options): Buffer;
...
}
从上面代码可以看出decode
并不是直接把乱码字符串传入,而是传入对应字符格式的Buffer
类型
Node.js Buffer(缓冲区)
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。
但在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。
在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可以让 Node.js 处理二进制数据,每当需要在 Node.js 中处理I/O操作中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。
可以再通过学习与使用熟悉该数据类型
responseType
通过对解决问题的思考方向转变后,我通过查看axios
源码找到了最终解决的接口
axios
源码
export interface AxiosInstance {
...
// post方法中的第三个变量config为AxiosRequestConfig接口格式类型
post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>;
...
}
export interface AxiosRequestConfig {
...
// 该类型提供了ResponseType类型的responseType属性
responseType?: ResponseType;
...
}
// ResponseType类型中可使用的参数如下,其中就有arraybuffer数据类型
export type ResponseType =
| 'arraybuffer'
| 'blob'
| 'document'
| 'json'
| 'text'
| 'stream'
查看axios
源码我们找到了使响应格式呈buffer
格式的接口,通过ResponseType
类型的定义还可以发现响应类型的设置还有其他几种类型