对于 restfulAPI,一般如何选择交换数据的格式呢? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
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 t/186561
IOS 开发怎么处理数据类型问题? t/140470
为什么……不拿另一个 HTTP 服务器来当作数据库来给一个 Web App 使用? 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     Solana     2525 人在线   最高记录 6679       Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 39ms UTC 15:06 PVG 23:06 LAX 08:06 JFK 11:06
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