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

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

  •  
  •   dcsuibian · 93 天前 · 1567 次点击
    这是一个创建于 93 天前的主题,其中的信息可能已经有所发展或是发生改变。

    据我所知,一个合法的 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

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

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

    {
    "avatar": "xxxxx"
    }



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

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

    目前是这么理解的,这样看来,跟什么 RFC 文档、axios 也没关系,单纯就是我对 XMLHttpRequest 不熟。。。
    cctrv
        11
    cctrv   93 天前 via iPhone
    你 json 又何以只有一個字符串呢?

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

    就是 9 樓那樣的⋯
    lujjjh
        12
    lujjjh   93 天前
    https://github.com/axios/axios/pull/3688/files#diff-b34f2f53ab94368c86775969fb604e8375abe03b6a378bdd09896fd91ac0a0d2R59-R64

    看了下 axios 最新的实现,已经会检测当请求 Content-Type 是 application/json 的时候自动 JSON.stringify 了(感觉是个 breaking change
    icyalala
        13
    icyalala   93 天前
    @cctrv 最初的 JSON 标准只允许顶层是 Object 或 Array,
    但是从 14 年的 RFC7159 开始,就支持顶层是普通的 Value 了,
    所以 "hello" 或者 1234 都算是合法的 JSON 。
    lujjjh
        14
    lujjjh   93 天前 via iPhone
    因此如果用 axios,尽量避免手动 stringify,而是重写掉默认的 transformRequest 来修复这个 BUG,否则未来升级 axios 会遇到重复 stringify 两次的问题
    yidinghe
        15
    yidinghe   92 天前 via Android
    JSON 要求以 object 为单位传输数据
    muzuiget
        16
    muzuiget   92 天前   ❤️ 1
    你这个问题根本不是 JSON 的问题,是 axios 自己处理参数的问题,axios 最终还是会调用原生的 fetch/XMLHttpRequest 函数去发送请求。

    所以看 axios 自己的文档就好,看 axios 的 data 参数,如果是字符串,就原样转发。如果是对象,就得强制转成字符串,而 JSON.stringify 是它的默认做法而已。跟 Content-Type 没什么关系,Content-Type 就是给服务端解析做参考而已。
    Huelse
        17
    Huelse   92 天前
    json 要求顶级节点应为 object 或 array `consisting of attribute–value pairs and arrays (or other serializable values)`
    lujjjh
        18
    lujjjh   92 天前
    鉴于上面还在争论 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\"" 了
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   913 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 21:27 · PVG 05:27 · LAX 13:27 · JFK 16:27
    ♥ Do have faith in what you're doing.