V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
dcsuibian
V2EX  ›  Java

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

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

    据我所知,一个合法的 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 库啥都得猜吗?再碰到个不想让转格式的用户,你俩打一架谁打赢了按谁的意思来?
    zhengjian
        9
    zhengjian  
       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 attribute–value 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   ·   我们的愿景   ·   实用小工具   ·   3218 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 11:56 · PVG 19:56 · LAX 04:56 · JFK 07:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.