想在 Json 请求中传单个原始值,应该怎么做? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dcsuibian
V2EX    Java

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

  •  
  •   dcsuibian 2021-08-27 21:04:01 +08:00 2988 次点击
    这是一个创建于 1510 天前的主题,其中的信息可能已经有所发展或是发生改变。

    据我所知,一个合法的 JSON 对象不仅可以是对象或数组,一些原始值也是可以的,比如字符串"foo"、数字3、布尔值true也应该都是合法的 JSON 值。

    但是今天遇到了一个问题,我想设计一个接口来修改用户的头像。(头像 avatar 在数据库里也就是一个字符串)

    想设计得尽量 RESTful 一点用了 PUT 方法,请求路径大概是这样:

    /api/users/{用户的 id}/avatar 

    虽然只是一个字符串,但想和其它接口一样都接收 JSON,返回 JSON 。所以不希望用text/plain而是一样采用application/json的 MIME 类型。我想,既然一个字符串也是合法的 JSON,那应该也没问题,因此就在 axios 的 data 里传了一个字符串:

    image-20210827203655179

    但是发现它给我自己转换掉了,而且不是JSON.stringify那种转换。

    我本以为请求体里会是"a single string",但结果居然是a single string

    image-20210827203932188

    这根本就不是合法的 JSON 啊。

    虽然再包一层也能解决,但很不舒服。

    后来查了一下,似乎是 JSON 的定义出了问题:

    image-20210827205946716

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

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

    {
    "avatar": "xxxxx"
    }



    ```
    dcsuibian
        10
    dcsuibian  
    OP
       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
        11
    cctrv  
       2021-08-28 00:16:55 +08:00 via iPhone
    你 json 又何以只有一字符串呢?

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

    就是 9 那的
    lujjjh
        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
    icyalala
        13
    icyalala  
       2021-08-28 01:18:04 +08:00
    @cctrv 最初的 JSON 标准只允许顶层是 Object 或 Array,
    但是从 14 年的 RFC7159 开始,就支持顶层是普通的 Value 了,
    所以 "hello" 或者 1234 都算是合法的 JSON 。
    lujjjh
        14
    lujjjh  
       2021-08-28 01:22:23 +08:00 via iPhone
    因此如果用 axios,尽量避免手动 stringify,而是重写掉默认的 transformRequest 来修复这个 BUG,否则未来升级 axios 会遇到重复 stringify 两次的问题
    yidinghe
        15
    yidinghe  
       2021-08-28 11:45:00 +08:00 via Android
    JSON 要求以 object 为单位传输数据
    muzuiget
        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 就是给服务端解析做参考而已。
    Huelse
        17
    Huelse  
       2021-08-28 20:17:54 +08:00
    json 要求顶级节点应为 object 或 array `consisting of attributevalue pairs and arrays (or other serializable values)`
    lujjjh
        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\"" 了
    kkeep
        19
    kkeep  
       2022-08-11 02:09:06 +08:00 via Android
    @knives 你这也不对,content type 只是声明,有人就喜欢自己序列化后放 string 给 data 呢
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5822 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 06:19 PVG 14:19 LAX 23:19 JFK 02:19
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86