V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
temberature
V2EX  ›  程序员

对于 restfulAPI,一般如何选择交换数据的格式呢?

  •  
  •   temberature · 2015-09-17 08:31:19 +08:00 via iPad · 11207 次点击
    这是一个创建于 3385 天前的主题,其中的信息可能已经有所发展或是发生改变。
    主要想请教类似 a=1&b=2 和{ a : 1 , b : 2 }这两种的区别
    第 1 条附言  ·  2015-09-17 09:53:02 +08:00
    使用场景: web 单页应用

    返回数据用 json 没疑问,问题在 post 某个数据时有这两种做法。

    我认为应该传 json ,这样收发数据统一,传递时也不用转换,另一种观点认为应该用 a=1&b=2 这种,有两个理由: 1 、比较省流量 2 、发或修改数据一般比较简单,完全可以表达
    64 条回复    2015-09-19 10:49:53 +08:00
    matthewgao
        1
    matthewgao  
       2015-09-17 08:55:57 +08:00 via Android
    通常用 json
    crs0910
        2
    crs0910  
       2015-09-17 08:56:33 +08:00
    这是什么意思?不是 GET 和 POST 吗?
    vainly
        3
    vainly  
       2015-09-17 08:57:09 +08:00
    如果是 GET 请求,请求参数要在请求地址后"http://ip:port/doman?a=1&b=1&c=1"
    如果是 POST 请求,而且交互使用 JSON 数据格式的话,参数传递会是这样的"{a:1,b=2,c=3,...}"
    mithvv
        4
    mithvv  
       2015-09-17 09:22:22 +08:00
    get
    a=1&b=2&arr[]=3&arr[]=4
    post
    {
    a:1,b:2,c:[3,4]
    }
    morethansean
        5
    morethansean  
       2015-09-17 09:24:52 +08:00
    POST 传出去不还是 a=1&b=c
    除非你是自定义的 value 值是 json 格式比如 a=1&b=%7Bc%3A%205%7D

    > decodeURIComponent ('%7Bc%3A%205%7D')
    > "{c: 5}"
    jhaohai
        6
    jhaohai  
       2015-09-17 09:32:03 +08:00
    json ,感觉已经成为标准了
    wshcdr
        7
    wshcdr  
       2015-09-17 09:33:13 +08:00
    json 已经是事实上的标准了
    learnshare
        8
    learnshare  
       2015-09-17 10:00:16 +08:00
    JSON 几乎作为标准存在了,虽然数据量会大一些
    temberature
        9
    temberature  
    OP
       2015-09-17 10:04:03 +08:00
    相关的帖子
    Json VS Protobuf https://www.v2ex.com/t/186561
    IOS 开发怎么处理数据类型问题? https://www.v2ex.com/t/140470
    为什么……不拿另一个 HTTP 服务器来当作数据库来给一个 Web App 使用? https://www.v2ex.com/t/120678
    morethansean
        10
    morethansean  
       2015-09-17 10:08:42 +08:00
    不管你提交是否是 JSON ,转换总是需要的,特别是提交数据肯定是需要经过服务器端校验和安全处理的。
    而且大多时候提交的数据又不是直接丢数据库去储存的,增删查找需要的字段,和返回的结果本来有些时候就相差甚远,处理逻辑也完全不一样,没必要强行 JSON 格式。
    temberature
        11
    temberature  
    OP
       2015-09-17 10:15:40 +08:00
    @morethansean 我说的转换是指这两种字符串能表达的数据结构是等价的,如果只有一种,只需要在一种交换数据和程序变量之间转换。我没有说一定要用什么,只是想知道请教下是否有区别,以及各自的利弊又是什么?
    temberature
        12
    temberature  
    OP
       2015-09-17 10:23:26 +08:00
    @learnshare 感觉都浪费在双引号上了
    matthewgao
        13
    matthewgao  
       2015-09-17 10:33:15 +08:00
    @vainly ”如果是 GET 请求,请求参数要在请求地址后"http://ip:port/doman?a=1&b=1&c=1" “ 这个似乎不是很符合 RESTful 的建议吧
    lyz1990
        14
    lyz1990  
       2015-09-17 10:36:19 +08:00
    json ,事实标准
    temberature
        15
    temberature  
    OP
       2015-09-17 10:40:55 +08:00
    用 JSON 构建 API 的标准指南
    http://jsonapi.org/
    http://jsonapi.org.cn/
    hantsy
        16
    hantsy  
       2015-09-17 10:46:29 +08:00   ❤️ 6
    @temberature 首先搞清楚什么是 REST 。

    InfoQ 上有 Fielding 博士的原始论文的中文翻译版本。

    广泛的讲 REST 不限于 HTTP 协议,数据交换格式可以是文本也可以是二进制。狭义的讲,我们通常将 REST 限于 HTTP , JSON 是取常用的数据交换格式, XML 也没问题。

    REST 的几个要素:
    1. 用一个唯一的 URI 代表资源的定位
    2. 用 HTTP 语义( GET , POST , PUT , PATCH , DELETE , HEAD , OPTION 等)操作资源
    3. 用 HTTP 状态标志操作结果

    常用的 CRUD REST API 可以这样设计:

    比如常见的 BLOG 程序,定义一个 /posts 入口。

    /posts , GET , 取得所有 POST ,结果是一个 [{},{}]数组,可以接查询参数?page=XXX 等,返回 200 状态。
    /posts , POST , 发布新 POST , Request Body 为{title:"text", body:"test body"}, 返回 201 状态。

    /posts /{id}, GET , 取得所有单个 POST , URI 需要接 id, 成功返回 200 状态,失败 404 。
    /posts /{id} , PUT , 更新 POST , Request Body 为{title:"text", body:"test body"}, 返回 204 状态。
    /posts /{id} , DELETE , 删除 POST , 成功 返回 204 状态。

    /posts 是对资源集合操作,/posts/{id}对单个资源操作。操作的结果有的有返回数据,更直接的表达结果是 HTTP Status 。

    我写的简单的例子, https://github.com/hantsy/angularjs-springmvc-sample
    这个例子,目前没写任何 Notes 来描述。

    要了解 REST API 实现,可以看另外一个类似的例子 https://github.com/hantsy/angularjs-grails-sample , 这个项目的 WIKI 上的详细介绍。

    @temberature 从 REST API 设计角度,你的问题和 REST 一点关系都没有。从设计上角度,查询数据?a=1 仅适用条件过滤,也就是上面第一个 /posts GET 方式。

    另外,关于 REST API 设计好坏,有一个 Rechardson 成熟度模型来衡量。它定义了三个级别。国内几个一线大公司的 API 基本都是 Level 0 , 按这个标准衡量来讲,都不是 REST API ,虽然他们 Developer 网站都喜欢写上 REST 的字样。反而一些不大的公司做得不错,比如 JPUSH 等。

    当我们考虑 Level3 ( Self-document, Self-Discovery ) 的时候,在交换格式上就有很多细节要去实现。这方面有一些行业标准或者正在标准化的交换格式。

    如: HAL , Collection+JSON , JSON API 。
    hantsy
        17
    hantsy  
       2015-09-17 10:51:09 +08:00
    @matthewgao GET 是用于查询操作,是可以使用 query parameters 的。
    hantsy
        18
    hantsy  
       2015-09-17 10:52:31 +08:00   ❤️ 1
    @temberature JSON API 目前应用范围不广,不如 HAL 流行, Spring 目前只支持 HAL 。
    refear99
        19
    refear99  
       2015-09-17 10:52:57 +08:00
    取就用 get+query
    增删改就用 POST DELETE PUT + json body
    temberature
        20
    temberature  
    OP
       2015-09-17 11:06:18 +08:00
    @hantsy 博士的论文我翻过几次,我知道数据交换并不是 rest 的主要关注点,只要想请教下在实际的做法以及如何取舍,感谢分享~
    hantsy
        21
    hantsy  
       2015-09-17 11:12:24 +08:00
    @temberature 我上面两个链接的例子中 UI 全部是 SPA 。
    gamexg
        22
    gamexg  
       2015-09-17 11:28:38 +08:00
    直接 command_json , sign 。

    a=1&b=2 这种做 hash 签名容易被坑死。
    temberature
        23
    temberature  
    OP
       2015-09-17 11:48:05 +08:00
    @gamexg 没太明白,能再具体解释下吗:)
    loading
        24
    loading  
       2015-09-17 11:56:21 +08:00 via Android
    如果省流量你考虑进去,这问题就没必要讨论了。
    hash 你后面掉坑就知道了。
    temberature
        25
    temberature  
    OP
       2015-09-17 12:08:29 +08:00
    @loading 清楚利弊,在具体情况下就好取舍了吧;能描述下这个坑的大小、颜色、气味吗?以免掉下水道里,哈哈~
    gamexg
        26
    gamexg  
       2015-09-17 12:31:24 +08:00
    比如一些 http 请求为了安全需要做 hash 签名, a=1&b=2 这种签名会碰到很多坑。

    看一下这个 hash 签名设计:
    https://blog.yanke.io/she-ji-yi-tao-ji-yu-hash-qian-ming-yan-zheng-de-api/

    ```
    因此我初步设计了一个签名机制,步骤如下:

    将时间戳,随机串, TokenID ,和业务逻辑关键参数构成一个字典
    将字典按照 Key 升序排序,然后按照 key1 value1 key2 value2 拼成一个连续字符串
    将 Path 拼在前面, Token 拼在后面
    将整个字符串做 SHA256 ,作为一个参数放回到请求中
    服务器端接收到请求的时候也进行同样的处理,进行签名验证。

    如果你曾经裸写过常见的第三方服务 API 调用的话,你会对这些步骤非常熟悉。
    ```

    如果直接传递 json 字符串,直接 hash json 字符串即可,不用麻烦的拼接了。
    temberature
        27
    temberature  
    OP
       2015-09-17 12:47:19 +08:00
    @gamexg 只理解到处理方式的差别,没看到坑
    wangleineo
        28
    wangleineo  
       2015-09-17 12:48:48 +08:00   ❤️ 1
    还是统一用 JSON 好些吧,用 Form 提交的话,多层数据怎么办?
    FrankFang128
        29
    FrankFang128  
       2015-09-17 12:49:53 +08:00
    都支持就好了
    temberature
        30
    temberature  
    OP
       2015-09-17 12:55:41 +08:00
    @FrankFang128 这样只是把问题抛给了调用接口方吧
    hantsy
        31
    hantsy  
       2015-09-17 12:56:33 +08:00
    @gamexg 难道不用 HTTPS 吗?签名这东西,看着就吐,根本谈不上安全,如果你的 Token 被截取(没 HTTPS 所有数据传输都透明), Hacker 构造你这样顺序的签名有什么难的。这也是 oAuth2 去掉了签名,强制使用 HTTPS 的原因。
    marginleft
        32
    marginleft  
       2015-09-17 12:59:30 +08:00
    pandada8
        33
    pandada8  
       2015-09-17 13:00:14 +08:00
    不是取决于 Accept 头的么……
    pandada8
        34
    pandada8  
       2015-09-17 13:04:40 +08:00
    @pandada8 哦没看清附言……
    主要看你的 spa 框架……比如 angular 默认 post 是 json ……
    总之怎么方便怎么来
    iyaozhen
        35
    iyaozhen  
       2015-09-17 13:20:46 +08:00
    这个取决于你的操作吧。 GET 获取数据,可以用 urlencode 传参。 POST 用于更改数据,传 json 更合适吧。
    temberature
        36
    temberature  
    OP
       2015-09-17 13:38:39 +08:00
    @pandada8 如果接口是不同的人在写,那还要看是谁方便,有一套参考的规范会更好吧
    aftereclipse
        37
    aftereclipse  
       2015-09-17 13:41:51 +08:00
    统一 HTTP POST , json 数据格式,客户端和服务端都好写,加解密也好做
    vainly
        38
    vainly  
       2015-09-17 14:04:11 +08:00
    @matthewgao 不是啊,对于指定参数的 GET 请求时这么做的。
    比如这样:
    @GET
    @Path ("findById")
    public String findById (@QueryParam ("id")String id );
    temberature
        39
    temberature  
    OP
       2015-09-17 14:09:02 +08:00
    @vainly 没看懂,╮(╯▽╰)╭
    pandada8
        40
    pandada8  
       2015-09-17 14:26:06 +08:00
    @temberature 最后当然要统一了……之前先做好协定。
    比如有 angular,superagent 之类的场合我倾向于使用 json
    有些时候框架搭配用 jquery 发请求那就用 form-urlencoded.
    总体而言就是怎么方便怎么来
    ————
    实在决定不了么
    来局昆特牌吧
    gamexg
        41
    gamexg  
       2015-09-17 14:28:20 +08:00
    @temberature 你需要保证前后端的排序、编码等是一致的。记得有一篇 API 文档提到过,个别语言的排序和 API 服务器不一致,需要特殊处理。


    @hantsy 即使上了 https ,我也会利用类似签名的方式而不会直接传递 API 密钥做身份认证的。例如目前对于设备的认证就是设备注册时分配一个 id 及密钥,每个请求都会被 hash 签名。密钥只会在设备注册时传递一次,之后不会传递,这个风险还是可以接受的。由于密钥不像密码,更换麻烦,后期即使加上 https ,也不会直接传递密钥。
    railgun
        42
    railgun  
       2015-09-17 14:30:22 +08:00
    json 适合用来描述复杂的数据格式
    如果只是简单的数据,用哪个都无所谓
    还是要从实际出发,比如有的接口是要传文件的,用 json 就不合适了
    FrankFang128
        43
    FrankFang128  
       2015-09-17 14:32:50 +08:00
    @temberature 支持两种方式的话,就没有问题了呀。 调用方怎么都不会错,怎么叫推给他们。
    FrankFang128
        44
    FrankFang128  
       2015-09-17 14:39:39 +08:00
    楼上说什么 JSON 是事实标准的说法有点问题,
    application/x-www-form-urlencoded; 也是标准啊。

    按 REST 来说,根据 Content-Type 的值为 application/x-www-form-urlencoded 还是 application/json 做下判断就好了。
    temberature
        45
    temberature  
    OP
       2015-09-17 15:54:58 +08:00
    @FrankFang128 我的意思不是把麻烦推给他们,是把分析两种方式的利弊推给了他们;在接口方,我倾向在理想情况把一种实现好,毕竟现实中资源和精力是有限的;同意都是标准的说法,但是事实标准是可能存在的。
    jokcy
        46
    jokcy  
       2015-09-17 15:56:50 +08:00
    @morethansean 你可以用 payload 的方式传数据
    temberature
        47
    temberature  
    OP
       2015-09-17 16:05:30 +08:00
    @pandada8 有时候确实会遇到自己难以决定或者双方难以一致的问题,这时候我的态度就先实现一种(直觉有时候也管用),在使用经验增长之后,答案自然就有了。 ps :突然想到“约定大于配置”。
    temberature
        48
    temberature  
    OP
       2015-09-17 16:10:06 +08:00
    @railgun 文件确实特殊;一个项目中应该都有复杂点的数据,统一的约定还是挺重要的吧
    temberature
        49
    temberature  
    OP
       2015-09-17 16:12:49 +08:00
    @gamexg hash 时 json 字符串中的顺序也需要注意吧
    temberature
        50
    temberature  
    OP
       2015-09-17 16:13:55 +08:00
    @jokcy 一直没太明白设计这个的目的
    gamexg
        51
    gamexg  
       2015-09-17 16:50:20 +08:00
    @temberature 客户端是对序列化后的字符串签名,服务端是先验证签名后反序列化,所以不涉及 key 顺序的问题。
    temberature
        52
    temberature  
    OP
       2015-09-17 17:12:29 +08:00
    @gamexg 也见过接口的字符串签名算法,其实也可以按 json 这种,不明白为什么要排序:)
    FrankFang128
        53
    FrankFang128  
       2015-09-17 17:34:22 +08:00
    我觉得没必要 2 选 1 的,两种同时存在是 OK 的,也是更为常见的。
    yield9tk
        54
    yield9tk  
       2015-09-17 17:44:34 +08:00
    两种可以同属存在。不同的压缩方式,在 accept 中设置即可
    railgun
        55
    railgun  
       2015-09-17 17:54:43 +08:00
    @temberature 那就 json 吧,适配性好一点,扩展也方便
    hantsy
        56
    hantsy  
       2015-09-17 17:58:23 +08:00
    @gamexg 一些 oAuth2 provider 提供了 JWT 支持, 连接的时候使用, Spring 内置了支持(只需要设置)。其他情况真的没看到签名的使用。国内 X 宝 X 度通篇的 Sign ,除了让 API 看起来像屎一样,如果它的加密方法和顺序被预先知道了,我不觉得是安全的。

    如果你想做所有请求还要加密, X509 应该可以了吧。我想只有银行级的安全才会有这种要求。
    Geoion
        57
    Geoion  
       2015-09-17 18:16:12 +08:00
    两个尖括号的流量也省。。。。。
    temberature
        58
    temberature  
    OP
       2015-09-17 18:23:41 +08:00
    @Geoion 对于用户量亿级的服务,还是有大差别的吧
    jokcy
        59
    jokcy  
       2015-09-17 18:23:42 +08:00
    @temberature content-type 不一样
    hantsy
        60
    hantsy  
       2015-09-17 20:28:16 +08:00
    @temberature 针对 Mobile 原生程序(比如 Android , IOS )的某些场合可以绕开 HTTP ,特别图片等数据量大的东西,使用更底层的协议通讯,效率更高。其它文本类的,全部用 JSON ,所有平台都通用。

    其实, REST API 的设计,只要参考 Github API , 或者 Heroku 的 API 就好了。设计好的 API ,从根到底下资源是一个树型结构,如 / 是根, /posts 是 Post 资源集合, /posts/2 是 ID 为 2 的单个资源,/posts/2/comments ,是指 ID 为 2 的 Post 下面的所有 Comments 。如果使用 HAL , 从根开始添加一些必要的 metadata 信息, 下面的所有的资源都应该是可以 Discovery 的,使用 HAL Browser 这样工具是可以可视化整个 API 结构,而不需要借助文档说明。
    morethansean
        61
    morethansean  
       2015-09-18 09:08:38 +08:00
    @jokcy web 单页应用直接 form data 不好么。
    jokcy
        62
    jokcy  
       2015-09-18 09:57:49 +08:00
    @morethansean 不是不好,而是根据你们自己的需求自己定义,你这边以 form 格式传后台就以 form 格式接收并转换,用 json 的好处就是如果你的后台用的是类似 node 这样天生支持 json 数据的,那么就无需转换了。
    matthewgao
        63
    matthewgao  
       2015-09-19 10:48:20 +08:00
    @hantsy 我再补充一下, REST 还要注意状态转移和等幂性
    matthewgao
        64
    matthewgao  
       2015-09-19 10:49:53 +08:00
    @hantsy 多谢,我之前还没看到任何标准和参考说道到这个是可以的,反倒刻意去回避这个
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1195 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 23:26 · PVG 07:26 · LAX 15:26 · JFK 18:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.