据我所知,一个合法的 JSON 对象不仅可以是对象或数组,一些原始值也是可以的,比如字符串"foo"
、数字3
、布尔值true
也应该都是合法的 JSON 值。
但是今天遇到了一个问题,我想设计一个接口来修改用户的头像。(头像 avatar 在数据库里也就是一个字符串)
想设计得尽量 RESTful 一点,用了 PUT 方法,请求路径大概是这样:
/api/users/{用户的 id}/avatar
虽然只是一个字符串,但想和其它接口一样都接收 JSON,返回 JSON 。所以不希望用text/plain
而是一样采用application/json
的 MIME 类型。我想,既然一个字符串也是合法的 JSON,那应该也没问题,因此就在 axios 的 data 里传了一个字符串:
但是发现它给我自己转换掉了,而且不是JSON.stringify
那种转换。
我本以为请求体里会是"a single string"
,但结果居然是a single string
:
这根本就不是合法的 JSON 啊。
虽然再包一层也能解决,但很不舒服。
后来查了一下,似乎是 JSON 的定义出了问题:
于是我就想问下,大佬们有遇到过这个问题吗?怎么优雅地解决这个问题呢?
1
codehz 2021-08-27 21:08:49 +08:00
不是,axios 这玩意是直接根据 data 的类型决定序列化方法的吧,毕竟得照顾直接按文本发的用例。。。
|
2
Trim21 2021-08-27 21:20:48 +08:00
{ data: '"string"'} 这样是一个以 JSON 编码的字符串
|
3
Trim21 2021-08-27 21:22:44 +08:00
@Trim21 #2 说的有点不清楚。axios({ data: '"string"'}) 这样发送的是一个以 JSON 编码的字符串
|
4
knives 2021-08-27 22:00:34 +08:00
所以这应该算 axios 的问题吧,没有完全根据 Content-Type 来决定 data 的序列化方式。具体代码见 https://github.com/axios/axios/blob/master/lib/defaults.js#L59
|
5
lianyue 2021-08-27 22:09:28 +08:00
为什么 不 直接传 new Blob
呢 |
6
dcsuibian OP @codehz 我在 headers 里面加了 Content-Type 里面加了 application/json,我以为会有作用的,现在看来好像就只是改了一下请求头而已
|
7
dcsuibian OP @Trim21 哦哦,感谢!
我的理解是既然他会帮我解析一下的话,那就让它解析完后还是个得到个字符串。 如果字符串是个变量 str 的话,那么应该写成 axios({ data: JSON.stringify( str ) }),这样想对吗? |
9
zhengjian 2021-08-27 23:06:34 +08:00
```
/api/users/:userId/profile { "avatar": "xxxxx" } ``` |
10
dcsuibian OP @Trim21 不对,我又理解错了。
刚刚去看了一下最基本的 XMLHttpRequest,XMLHttpRequest.send 发送一个字符串时就是把字符串的内容直接放在了请求体里。这样想的话,axios 遇到字符串时应该就是一样的操作,没有做任何解析。而使用 xhr 直接发送一个对象的话,测试了下会变成[object Object]这样的(应该是 toString )的。所以猜测 axios 对 JavaScript 对象的处理做了优化。 而我原来以为它是知道我要传输 JSON,再检测我在 data 里放的数据的类型后,进行一定的处理,比如检测到是字符串后,在放进请求体之前加双引号和斜杠之类的。 目前是这么理解的,这样看来,跟什么 RFC 文档、axios 也没关系,单纯就是我对 XMLHttpRequest 不熟。。。 |
11
cctrv 2021-08-28 00:16:55 +08:00 via iPhone
你 json 又何以只有一個字符串呢?
你只能 { "avatar": "xxxxx" } 就是 9 樓那樣的⋯ |
12
lujjjh 2021-08-28 01:14:55 +08:00
https://github.com/axios/axios/pull/3688/files#diff-b34f2f53ab94368c86775969fb604e8375abe03b6a378bdd09896fd91ac0a0d2R59-R64
看了下 axios 最新的实现,已经会检测当请求 Content-Type 是 application/json 的时候自动 JSON.stringify 了(感觉是个 breaking change |
13
icyalala 2021-08-28 01:18:04 +08:00
@cctrv 最初的 JSON 标准只允许顶层是 Object 或 Array,
但是从 14 年的 RFC7159 开始,就支持顶层是普通的 Value 了, 所以 "hello" 或者 1234 都算是合法的 JSON 。 |
14
lujjjh 2021-08-28 01:22:23 +08:00 via iPhone
因此如果用 axios,尽量避免手动 stringify,而是重写掉默认的 transformRequest 来修复这个 BUG,否则未来升级 axios 会遇到重复 stringify 两次的问题
|
15
yidinghe 2021-08-28 11:45:00 +08:00 via Android
JSON 要求以 object 为单位传输数据
|
16
muzuiget 2021-08-28 16:58:12 +08:00 1
你这个问题根本不是 JSON 的问题,是 axios 自己处理参数的问题,axios 最终还是会调用原生的 fetch/XMLHttpRequest 函数去发送请求。
所以看 axios 自己的文档就好,看 axios 的 data 参数,如果是字符串,就原样转发。如果是对象,就得强制转成字符串,而 JSON.stringify 是它的默认做法而已。跟 Content-Type 没什么关系,Content-Type 就是给服务端解析做参考而已。 |
17
Huelse 2021-08-28 20:17:54 +08:00
json 要求顶级节点应为 object 或 array `consisting of attribute–value pairs and arrays (or other serializable values)`
|
18
lujjjh 2021-08-28 20:44:16 +08:00
鉴于上面还在争论 JSON 的问题
1. JSON value 当然可以是 primitive value ( https://www.json.org) 2. Content-Type 标明了 JSON,一个上层的 HTTP 库当然可以自动对 data 做 stringify,即便 data 是 primitive value ; axios 没有对 primitive value 自动 stringify 是设计缺陷,并且已经被修复 ( https://github.com/axios/axios/pull/3688),但还没有发版 3. 如果你用 stringify 来 workaround,当 2 中的修改被发版之后,会产生 stringify 两次的问题,到时候 payload 就变成 "\"a single string\"" 了 |