想在 Json 请求中传单个原始值,应该怎么做?

2021-08-27 21:04:01 +08:00
 dcsuibian

据我所知,一个合法的 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 的定义出了问题:

于是我就想问下,大佬们有遇到过这个问题吗?怎么优雅地解决这个问题呢?

2535 次点击
所在节点    Java
19 条回复
codehz
2021-08-27 21:08:49 +08:00
不是,axios 这玩意是直接根据 data 的类型决定序列化方法的吧,毕竟得照顾直接按文本发的用例。。。
Trim21
2021-08-27 21:20:48 +08:00
{ data: '"string"'} 这样是一个以 JSON 编码的字符串
Trim21
2021-08-27 21:22:44 +08:00
@Trim21 #2 说的有点不清楚。axios({ data: '"string"'}) 这样发送的是一个以 JSON 编码的字符串
knives
2021-08-27 22:00:34 +08:00
所以这应该算 axios 的问题吧,没有完全根据 Content-Type 来决定 data 的序列化方式。具体代码见 https://github.com/axios/axios/blob/master/lib/defaults.js#L59
lianyue
2021-08-27 22:09:28 +08:00
为什么 不 直接传 new Blob
dcsuibian
2021-08-27 22:19:32 +08:00
@codehz 我在 headers 里面加了 Content-Type 里面加了 application/json,我以为会有作用的,现在看来好像就只是改了一下请求头而已
dcsuibian
2021-08-27 22:26:16 +08:00
@Trim21 哦哦,感谢!
我的理解是既然他会帮我解析一下的话,那就让它解析完后还是个得到个字符串。
如果字符串是个变量 str 的话,那么应该写成 axios({ data: JSON.stringify( str ) }),这样想对吗?
ch2
2021-08-27 22:36:28 +08:00
@dcsuibian 不然呢,http 库啥都得猜吗?再碰到个不想让转格式的用户,你俩打一架谁打赢了按谁的意思来?
zhengjian
2021-08-27 23:06:34 +08:00
```
/api/users/:userId/profile

{
"avatar": "xxxxx"
}



```
dcsuibian
2021-08-27 23:19:07 +08:00
@Trim21 不对,我又理解错了。
刚刚去看了一下最基本的 XMLHttpRequest,XMLHttpRequest.send 发送一个字符串时就是把字符串的内容直接放在了请求体里。这样想的话,axios 遇到字符串时应该就是一样的操作,没有做任何解析。而使用 xhr 直接发送一个对象的话,测试了下会变成[object Object]这样的(应该是 toString )的。所以猜测 axios 对 JavaScript 对象的处理做了优化。

而我原来以为它是知道我要传输 JSON,再检测我在 data 里放的数据的类型后,进行一定的处理,比如检测到是字符串后,在放进请求体之前加双引号和斜杠之类的。

目前是这么理解的,这样看来,跟什么 RFC 文档、axios 也没关系,单纯就是我对 XMLHttpRequest 不熟。。。
cctrv
2021-08-28 00:16:55 +08:00
你 json 又何以只有一個字符串呢?

你只能
{
"avatar": "xxxxx"
}

就是 9 樓那樣的⋯
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
icyalala
2021-08-28 01:18:04 +08:00
@cctrv 最初的 JSON 标准只允许顶层是 Object 或 Array,
但是从 14 年的 RFC7159 开始,就支持顶层是普通的 Value 了,
所以 "hello" 或者 1234 都算是合法的 JSON 。
lujjjh
2021-08-28 01:22:23 +08:00
因此如果用 axios,尽量避免手动 stringify,而是重写掉默认的 transformRequest 来修复这个 BUG,否则未来升级 axios 会遇到重复 stringify 两次的问题
yidinghe
2021-08-28 11:45:00 +08:00
JSON 要求以 object 为单位传输数据
muzuiget
2021-08-28 16:58:12 +08:00
你这个问题根本不是 JSON 的问题,是 axios 自己处理参数的问题,axios 最终还是会调用原生的 fetch/XMLHttpRequest 函数去发送请求。

所以看 axios 自己的文档就好,看 axios 的 data 参数,如果是字符串,就原样转发。如果是对象,就得强制转成字符串,而 JSON.stringify 是它的默认做法而已。跟 Content-Type 没什么关系,Content-Type 就是给服务端解析做参考而已。
Huelse
2021-08-28 20:17:54 +08:00
json 要求顶级节点应为 object 或 array `consisting of attribute–value pairs and arrays (or other serializable values)`
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\"" 了
kkeep
2022-08-11 02:09:06 +08:00
@knives 你这也不对,content type 只是声明,有人就喜欢自己序列化后放 string 给 data 呢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/798414

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX