虚拟研讨会:如何设计好的 RESTful API?

阅读数:27804 2013 年 7 月 25 日 13:57

REST 架构风格最初由 Roy T. Fielding(HTTP/1.1 协议专家组负责人)在其 2000 年的博士学位论文中提出。HTTP 就是该架构风格的一个典型应用。从其诞生之日开始,它就因其可扩展性和简单性受到越来越多的架构师和开发者们的青睐。它必将得到更大的发展。一方面,随着云计算和移动计算的兴起,许多企业愿意在互联网上共享自己的数据、功能;另一方面,在企业中,RESTful API(也称RESTful Web 服务)也逐渐超越SOAP 成为实现SOA 的重要手段之一。为此,InfoQ 将通过一系列深度文章和虚拟研讨的形式,深入剖析REST,以飨读者。

设计好 RESTful API 对于软件架构的可扩展性、可伸缩性和消费者的体验都具有至关重要的作用。本次虚拟研讨会的主题是,如何设计好的 RESTful API。我们邀请了几位国内对 REST 风格架构及 RESTful API 设计颇有心得的专家,请他们就自己的实战经验谈谈对这一问题的看法。他们分别是李锟、李建业、丁雪丰、马钧(注:文末有他们的个人简介)。

问题

我们为本次虚拟研讨会设计的问题有:

  1. 什么是好的 RESTful API?相信每个人都有自己的评判标准。那么,您认为一个好的 RESTful API 应该具有哪些特征呢?
  2. 安全是恒久的话题,对于基于 WSDL 和 SOAP 的 Web Service,我们有 WS-Security 这样的安全规范来指导实现认证、授权、身份管理等安全需求。那么,RESTful API 有无成熟可用规范或实现框架呢?如何保证 RESTful API 的安全性呢?
  3. 如何对 RESTful API 进行版本控制,请分享您认为实用的做法?
  4. HTTP1.1 规范中给出的动词对于设计 RESTful API 够用吗?您在实际项目中会扩展自己的动词吗?在什么情况下需要扩展?
  5. 今年 5 月份发布的 JAX-RS 2.0 规范对于 RSTfulAPI 的设计最有价值的特性是哪个(些)? 它(们)用于解决什么问题?
  6. 能否为 InfoQ 的读者们推荐一款实用的 RESTful API 开发框架,并说明您的推介理由。
  7. HTTP2.0 规范正在制定当中,您对它的期待是什么?

InfoQ:什么是好的 RESTful API?相信每个人都有自己的评判标准。那么,您认为一个好的 RESTful API 应该具有哪些特征呢?

李锟:一个好的 RESTful API,应该具备以下特征:

  1. 这个 API 应该是对浏览器友好的,能够很好地融入 Web,而不是与 Web 格格不入。

    浏览器是最常见和最通用的 REST 客户端。好的 RESTful API 应该能够使用浏览器 +HTML 完成所有的测试(不需要使用编程语言)。这样的 API 还可以很方便地使用各种自动化的 Web 功能测试、性能测试工具来做测试。Web 前端应用(基于浏览器的 RIA 应用、移动 App 等等)也可以很方便地将多个 RESTful API 的功能组合起来,建造 Mashup 类的应用。

  2. 这个 API 中所包含的资源和对于资源的操作,应该是直观和容易理解的,并且符合 HTTP 协议的要求。

    REST 开发又被称作“面向资源的开发”,这说明对于资源的抽象,是设计 RESTful API 的核心内容。RESTful API 建模的过程与面向对象建模类似,是以名词为核心的。这些名词就是资源,任何可命名的抽象概念都可以定义为一个资源。而 HTTP 协议并不是一种传输协议,它实际提供了一个操作资源的统一接口。对于资源的任何操作,都应该映射到 HTTP 的几个有限的方法(常用的有 GET/POST/PUT/DELETE 四个方法,还有不常用的 PATCH/HEAD/OPTIONS 方法)上面。所以 RESTful API 建模的过程,可以看作是具有统一接口约束的面向对象建模过程。

    按照 HTTP 协议的规定,GET 方法是安全且幂等的,POST 方法是既不安全也不幂等的(可以用来作为所有写操作的通配方法),PUT、DELETE 方法都是不安全但幂等的。将对资源的操作合理映射到这四个方法上面,既不过度使用某个方法(例如过度使用 GET 方法或 POST 方法),也不添加过多的操作以至于 HTTP 的四个方法不够用。

    如果发现资源上的操作过多,以至于 HTTP 的方法不够用,应该考虑设计出更多的资源。设计出更多资源(以及相应的 URI)对于 RESTful API 来说并没有什么害处。

  3. 这个 API 应该是松耦合的。

    RESTful API 的设计包括了三个循序渐进、由低到高的层次:资源抽象、统一接口、超文本驱动。正是这三个层次确保了 RESTful API 的松耦合性。

    当设计面向互联网的 API 时,松耦合变成了一种“必须有”的强需求。紧耦合的 API 非常脆弱,一旦公布出去,服务器端和客户端都无法持续进化。尤其是服务器端,公布出去的接口根本不敢改,改了之后,几乎所有客户端应用立即无法正常工作。REST 这种架构风格就是紧耦合 API 的解毒剂,这个话题可以谈的很深,这里就不展开了。感兴趣的读者可以参考《REST 实战》。

  4. 这个 API 中所使用的表述格式应该是常见的通用格式

    在 RESTful API 中,对于资源的操作,是通过在服务器端 - 客户端之间传递资源的表述来间接完成的。资源的表述可以有很多种格式,并且在响应和请求中的资源表述格式也会有所不同。GET/POST 响应中的资源表述格式,常见的有 HTML、XML、JSON;POST/PUT 请求中的资源表述格式,常见的有标准的 HTML 表单参数、XML、JSON。

    这些常见表述格式,处理起来非常容易,有大量的框架和库提供支持。所以除非有很合理的要求,通常不需要使用自定义的私有格式。

  5. 使用 HTTP 响应状态代码来表达各种出错情况

    HTTP 响应状态代码,是 HTTP 协议这个统一接口中用来表达出错情况的标准机制。响应状态代码分成两部分:status code 和 reason phase。两部分都是可定制的,也可以使用标准的 status code,只定制 reason phase。

    如果一个所谓的“RESTful API”对于任何请求都返回 200 OK 响应,在响应的消息体中返回出错情况信息,这种做法显然不符合“确保操作语义的可见性”这个 REST 架构风格的基本要求。

  6. 这个 API 应该对于 HTTP 缓存是友好的

    充分利用好 HTTP 缓存是 RESTful API 可伸缩性的根本。HTTP 协议是一个分层的架构,从两端的 user agent 到 origin server 之间,可以插入很多中间组件。而在整个 HTTP 通信链条的很多位置,都可以设置缓存。HTTP 协议内建有很好的缓存机制,可以分成过期模型和验证模型两套缓存机制。如果 API 设计者完全没有考虑过如何利用 HTTP 缓存,那么这个 API 的可伸缩性会有很多问题。

李建业:首先说明一下,对 REST 这个概念,我一般把它理解为 REST 风格的架构,但是现在实践中最为广泛认知的是 HTTP,而它是 REST 的一个实现,所以 RESTful API 也可以不太严格的指基于 HTTP 的 API——当然,即使是不严格的时候,API 本身也应该力求遵循 REST 架构风格。

我认为,一个 RESTful API 最重要的一点应该是——尽可能少的先验信息”,这一条也同时是我判断一个好的 RESTful API 的标准。

比如 HTTP 动词,在实践中,大家可能会常常纠结于有效利用 HTTP 动词,但这却并不是特别重要的事情——除非你理解这么做的价值。HTTP 动词最重要的地方在于它是标准阐明了的行为,也就是说,如果我们的“客户端”遵循约定,那么就不必要发明新的动词,也就不必增加“先验信息”;但是,所谓“先验信息”,针对的是客户端——对 API 来说就是调用者,对于一些企业内部系统,或者一些传统系统,由于“资源”很稳定,对资源的操作也很稳定,这些系统的“调用客户端”不是浏览器而是另一个系统,此时如果强制对应到 HTTP 动词,反而会变成额外的“先验信息”,这时我就不会太拘泥 HTTP 动词,自己制定一套动词放在参数中也可以接受——只要动词不变化,这个系统依然是 REST 风格的。

再比如 Response 里面的 Content-Type,这个有时会被新手忽略,但这其实很重要,因为一般涉及到系统间协同的 API,往往不会使用普通的文本,比较常见的是使用 json 表达复杂结构,而这与通常的缺省理解不同(缺省一般会认为是 text/plain 和 text/html),所以如果在 API 中忘记用 Content-Type 进行区分的话,后续对多种类型的客户端接入的支持就会变成陷阱(我们多次遇到过这个问题)。而如果一开始就检查是否增加先验知识(缺省 Content-Type 为 plain 或者允许指定 Content-Type),那这一困难就可以避免了。

丁雪丰:首先,应该正确地使用 HTTP 的统一接口,比如 HTTP 的动词,如果不分青红皂白清一色 POST 那显然还有改进的余地;

其次,资源有合适的粒度,可以从三个方面来评判资源的粒度是否合理——网络的效率、表述的大小以及客户端使用时的易用程度;

最后,是表述的设计,除了表述的正文内容,还有其中的 URI 和链接,这些都是评判一个 RESTful API 好坏的标准。

马钧:在我看来,一个好的 API 标准,就是能尽量利用到 HTTP 协议的特性,将 HTTP 当成一种转移协议,而不是传输协议。包括但不限于:利用 HTTP 的各种动词来明确操作;包含有内容协商,可以根据请求头提供的参数选择一个资源最合适的媒体类型、语言、字符集和编码的表现;使用不同的返回代码来描述各种状态。但实际上见到过的很多声称 RESTful API,包括国内的和国外的,能符合这些条件的并不多。 parse.com 提供的 API 是我见到过的较为不错的 RESTful API,可以作为范例参考。

InfoQ:安全是恒久的话题,对于基于 WSDL 和 SOAP 的 Web Service,我们有 WS-Security 这样的安全规范来指导实现认证、授权、身份管理等安全需求。那么,RESTful API 有无成熟可用规范或实现框架呢?如何保证 RESTful API 的安全性呢?

李锟:保证 RESTful API 的安全性,主要包括三大方面:

a) 对客户端做身份认证

b) 对敏感的数据做加密,并且防止篡改

c) 身份认证之后的授权

对客户端做身份认证,有几种常见的做法:

  1. 在请求中加签名参数

    为每个接入方分配一个密钥,并且规定一种签名的计算方法。要求接入方的请求中必须加上签名参数。这个做法是最简单的,但是需要确保接入方密钥的安全保存,另外还要注意防范 replay 攻击。其优点是容易理解与实现,缺点是需要承担安全保存密钥和定期更新密钥的负担,而且不够灵活,更新密钥和升级签名算法很困难。

  2. 使用标准的 HTTP 身份认证机制

    HTTP Basic 身份认证安全性较低,必须与 HTTPS 配合使用。HTTP Digest 身份认证可以单独使用,具备中等程度的安全性。

    HTTP Digest 身份认证机制还支持插入用户自定义的加密算法,这样可以进一步提高 API 的安全性。不过插入自定义加密算法在面向互联网的 API 中用的不是很多。

    这个做法需要确保接入方“安全域 - 用户名 - 密码”三元组信息的安全保存,另外还要注意防范 replay 攻击。

    优点:基于标准,得到了广泛的支持(大量 HTTP 服务器端、客户端库)。在服务器端做 HTTP 身份认证的职责可以由 Web Server(例如 Nginx)、App Server(例如 Tomcat)、安全框架(例如 Spring Security)来承担,对应用开发者来说是透明的。HTTP 身份认证机制(RFC 2617)非常好地体现了“分离关注点”的设计原则,而且保持了操作语义的可见性。

    缺点:这类基于简单用户名 + 密码机制的安全性不可能高于基于非对称密钥的机制(例如数字证书)。

  3. 使用 OAuth 协议做身份认证

    OAuth 协议适用于为外部应用授权访问本站资源的情况。其中的加密机制与 HTTP Digest 身份认证相比,安全性更高。需要注意,OAuth 身份认证与 HTTP Digest 身份认证之间并不是相互取代的关系,它们的适用场景是不同的。OAuth 协议更适合于为面向最终用户维度的 API 提供授权,例如获取隶属于用户的微博信息等等。如果 API 并不是面向最终用户维度的,例如像七牛云存储这样的存储服务,这并非是 OAuth 协议的典型适用场景。

对敏感的数据做加密,并且防止篡改,常见的做法有:

  1. 部署 SSL 基础设施(即 HTTPS),敏感数据的传输全部基于 SSL。
  2. 仅对部分敏感数据做加密(例如预付费卡的卡号 + 密码),并加入某种随机数作为加密盐,以防范数据被篡改。

身份认证之后的授权,主要是由应用来控制。通常应该实现某种基于角色 + 用户组的授权机制,这方面的框架有不少(例如 Spring Security),不过大多数开发团队还是喜欢自己来实现相关功能。

李建业:我不认为安全是 RESTful API 需要考虑的问题,事实上我觉得这是两个正交的问题。当然,如果使用 RESTful API 来提供认证、授权和身份管理,那也算是双方有关系,但是这和其它风格的 API 设计所要考虑的问题似乎没什么区别,不值得特别注意。

但是在具体设计层面,这两者的“正交点”上似乎确实有些问题,因为 REST 是一个推崇状态无关原则的架构风格,而认证和授权通常基于第三方解决方案,所以往往会出现违背有状态约束的问题,这个地方我也没有特别的想法,当然这个困难和原问题关系不大。

至于 WS- 族的协议,我不太了解,不太能参与讨论。

丁雪丰:对于 RESTful API,常见的安全措施都是可以继续使用的。例如,为了防篡改,可以对全部参数进行签名;为了防范重放攻击可以在请求中增加一次性的 Token,或者短时间内有效的 Token;对内容加密可以实现数据防泄露……;对于 DDoS 攻击,各种 HTTP 流量清洗策略,都可以继续发挥作用,因为这就是基本的 HTTP 请求。

在授权和认证方面,OAuth 2.0 已经基本成熟了,并且得到了广泛地应用。如果可以,接入第三方账户体系是个不错的选择,比如 Google 和 Facebook 的,国内的当然也有几个候选。

马钧:个人认为 RESTful 的安全性分为几个层次,在安全要求较高的场合,可以通过 HTTPs 这样的加密协议来保证网络层的安全,应用层的安全可以通过 OAuth 实现认证,而对于资源的访问授权,则只能依靠应用程序来实现了。

InfoQ:如何对 RESTful API 进行版本控制,请分享您认为实用的做法?

李锟:一个比较简单实用的做法是直接在 URI 中插入版本号,这样做允许多个版本的 API 并行运行。

另一个做法是在 HTTP 请求中加入自定义头信息,标明使用的版本号。不过这个做法其实对浏览器不够友好,简单地使用浏览器 +HTML 无法测试。

李建业:目前比较好的方式还是在 uri 设计中添加版本信息,其它方法都不如这个实用。

丁雪丰:个人认为最好的版本化,就是没有明显的版本。在对已发布的服务进行变更时,要尽量做到兼容,其中包括 URI、链接和各种不同的表述的兼容,最关键的就是在扩展时不能破坏现有的客户端。例如,要变更一个参数,可以选择同时兼容新旧两种输入,或者保持老参数不动,提供一个新的参数,在文档中必须做出说明,不推荐新用户再继续使用之前的参数。

如果必须要进行不兼容的变更,那么可以选择标记不同的版本号,这时可以选择在路径或参数中增加版本信息。也有做法是增加 HTTP 标头,只是在调用时会稍有不便,推荐前两种方法。

马钧:RESTfulAPI 的版本升级,尽量兼容之前的版本,保证原有的 API 都能正常工作,可以通过 HTTP 301 转跳到新的资源。另外一种实用的做法就是在 url 中保留版本号,同时提供多个版本供客户端使用,如 v1.rest.com 或者 rest.com/v1/ 这样。

InfoQ:HTTP1.1 规范中给出的动词对于设计 RESTful API 够用吗?您在实际项目中会扩展自己的动词吗?在什么情况下需要扩展?

李锟:这个问题取决于设计者如何看待和设计资源。如果资源抽象做的很好,对于某个资源的任何操作,通常都能够映射到 CRUD 四个类别中。CRUD 四个类别对于操作资源来说,绝大多数情况下是完备的。HTTP 的 GET/POST/PUT/DELETE 四个方法,对于 CRUD 四个类别的操作来说是足够的,映射关系是 Create-POST/Retrieve-GET/Update-PUT/Delete-DELETE。

我们通常不会选择创建自己的动词,这样做对于客户端开发者来说,需要更多的学习成本。如果在资源上定义的操作过多,我们会选择拆分出更多的资源。

李建业:一般是够用的,有时一些“不够用”的场景是由于我们没有设计出合理的资源,比如批量操作。但是,正如之前所说的那样,对于某些内部的、传统的(因此模型稳定且已知)系统,API 提供者和调用者会有自已的固定动词表,此时没必要拘泥。另外,我不建议扩展动词,一旦扩展了动词,其实已经破坏了我之前说的*“尽可能少的先验信息”*,那么,扩展动词和重新设计动词的成本差别不大。基于这个考虑,我建议尽可能保持动词不变,除非你想重新设计动词表。

丁雪丰:一般情况下,常用的 HTTP 动词是够用的,并没有出现一定要自己扩展动词的情况。其实,最常用的也就是 GET、POST、DELETE 和 PUT,而 HEAD、OPTIONS、TRACE 则基本用不太到。如果出现一时找不到合适的动词,安全幂等的操作用 GET,其他都可以用 POST,在设计资源时稍加考虑即可。

马钧:在我的实际项目中,只用到了 POST,PUT,DELETE,GET 这四个动词。

InfoQ:今年 5 月份发布的 JAX-RS 2.0 规范对于 RSTfulAPI 的设计最有价值的特性是哪个(些)? 它(们)用于解决什么问题?

李锟:REST 开发框架 RESTEasy 项目负责人 Bill Burke,去年写了一篇文章介绍JAX-RS 2.0

我同意 Bill 在文章中的观点,在 JAX-RS 2.0 增加的内容中,最重要的三部分为:

a) Client API——用来规范化 JAX-RS 客户端的开发方式。

b) Server-side Asynchronous HTTP——用来实现服务器端推送功能,而不需要依靠低效的轮询方式。

c) Filters and Interceptors——用来分离关注点,将鉴权、日志等逻辑与业务逻辑分离开,更好地实现代码重用。

这三部分的内容对于开发者来说都很有用。遵循 JAX-RS 规范做开发,可以确保服务器端以及客户端代码的可移植性。

李建业:我个人关注异步 API 这部分,主要是因为流式服务将会越来越多,那将大量需要这类支持。

InfoQ:能否为 InfoQ 的读者推荐一款实用的 RESTful API 开发框架,并说明您的推介理由。

李锟:这个问题我就不详细回答了。不同的编程语言有不同的 REST 开发框架,对于 REST 的支持程度也不同。开发 RESTful API 的需求范围很广,可选择的开发框架的范围也很广。保持多样性是繁荣生态环境的基础。像 Java 就有支持 JAX-RS 规范的 Jersey、RESTEasy、Restlet、Apache CXF,和不支持 JAX-RS 规范的 Spring MVC 等等很多框架。这些框架目前都做的不错。我对框架的选择没有倾向性。RESTful API 设计的最佳实践应该是通用的,而不是必须依赖某种特定的开发框架。

李建业:不好意思,这个我不太重视,没法推荐,不过我可以解释一下为什么对 RESTful API 框架不感冒的原因。

REST 作为一个架构风格,对我们的系统开发有很大影响,但是这些影响一般是针对架构(例如状态无关)或者设计(例如资源识别)上的,所以一旦涉及到具体实现,主要工作就基本结束了,此时开发框架能做的事也就只有简化编程了(相较而言,有的框架还能起到引导设计的作用),而由于 RESTful 会抽象动词,所以实现层面中和 API 规范相关的工作本来就不多,那么框架的价值就更小了。

当然,我们也不可能直接基于 servlet/rakc/wsgi 来开发,不过一般的编程语言都会提供一些简单的 url route/match 策略,我们使用这些就足够了。另外,有些框架能帮我们生成全部的动词支持,但这也未必是好事,我一般倾向于按需实现——用到了再支持,这就更不需要太关注开发框架对 RESTful 的支持了。

丁雪丰:由于本人是 Spring 的拥护者,工作中也一直在使用 Spring,所以在选择框架时会更多地倾向 Spring MVC(并不是说别的框架不好,这里有些个人主观的成份)。如果一定要选择其他框架,也要选择能够方便与 Spring 集成的框架。如果在项目中已经使用了 Spring,那么没有什么理由不选择 Spring MVC,鉴于目前 Spring 在各种项目中的高出镜率,相信一般情况下都会选择 Spring MVC。

REST 的成熟度模型中,第三层就是 HATEOAS,Spring 目前还提供了 Spring Hateoas 子项目,对链接、资源等方面的支持都做了一定的增强。

马钧:我目前在实际项目中使用的是 Spray,这是一个开源的 REST/HTTP 工具包和底层网络 IO 包,基于 Scala 和 Akka 构建。轻量级、异步、非堵塞、基于 actor 模式、模块化和可测试是 Spray 的特点。

InfoQ:HTTP2.0 规范正在制定当中,您对它的期待是什么?

李锟:我的期待包括两个方面:应该做的和不应该做的。

HTTP/2.0 规范应该做的:

  1. 与 HTTP/1.1 协议保持兼容。兼容的含义是说两者可以并存,客户端应用可以根据服务器端的能力,自由地选择使用 HTTP/2.0 还是 HTTP/1.1,而且选择过程对应用来说是透明的。
  2. 改进 HTTP 协议(作为资源的统一接口)之中操作语义表达方式的语法,提高网络传输效率。
  3. 更好地模块化,这样 HTTP/2.0 协议的实现能够更好地模块化。应用程序可根据需要选择适当的模块,而不是要么全有、要么全无。
  4. 废弃掉 HTTP/1.1 协议中一些很少有人用到的部分,例如采用管道(pipelining)方式发送请求。
  5. 增加更多的动词,以适应除 CRUD 之外的其他场景。

HTTP/2.0 规范不应该做的:

  1. HTTP/2.0 协议不应该把底层的数据加密机制(即 SSL)作为必选项。
  2. HTTP/2.0 协议不应该背离 REST 架构风格的约束,尤其是要确保操作语义对于中间组件的可见性。

在上面这两个方面,Roy Fileidng 曾经与 SPDY 协议设计者 Mike Belshe 发生过激烈争论,详情请看: Roy Fielding 谈 Google SPDY 协议

李建业:对此规范关注不多,不知道会不会有对于的支持,目前我所知道的只有 chunk 方式进行简单的支持,但是真正的流需要区分数据通道和控制通道——哪怕是逻辑上的区分,这样就直接对 REST 风格产生了很大冲击,考虑到流式服务在未来的发展潜力,我特别期待业界在这方面有所进展。

丁雪丰:HTTP 2.0 很大程度上是借鉴了 Google 的 SPDY,就我而言,首先,希望这个规范能做到与 HTTP 1.1 的兼容,使用者如果只认识 1.1,那么 2.0 能优雅“降级”;其次,希望 2.0 能带来更好的性能,SPDY 在这方面还是有所改进的,希望 HTTP 2.0 能再接再厉;最后,希望这个规范能在最终定稿时附带一个最佳实践,正确引导人们合理地使用 HTTP 2.0。

马钧:没研究过,估计即使出来,1.1 还有很长的生命周期,不会很快被取代。

关于专家们

李锟:从 2003 年开始尝试 Ajax 开发,意识到这种开发方式的优点后,次年(2004 年)在知名技术论坛 JavaEye 上与技术爱好者深入讨论过相关问题。他也是轻量级 Java 开发框架的拥护者,曾参与 JavaEye 论坛组织的《J2EE Development without EJB》一书的翻译。在 2007 年 Ruby on Rails 1.2 版支持 REST 开发之后,他对搞清楚 REST 的来龙去脉产生了浓厚兴趣,同年组织翻译了 Roy T. Fielding 的博士论文《Architectural Styles and the Design of Network-based Software Architectures》(中文版名为《架构风格与基于网络的软件架构设计》)。在 2011 年又翻译了《REST 实战》一书,目前仍然致力于在国内普及对于 REST 架构风格的深入理解,以及相关实战经验的交流。目前主要感兴趣的技术领域包括 DDD、DSL、SOA、REST。

李建业:2002 年毕业,之后一直从事软件开发,涉及办公自动化、电信网管 / 增值业务系统以及互联网;2009 年 12 月加入淘宝的广告应用开发团队;从 2011 年底开始,关注软件研发本身,涉及运维和测试相关工作,目前关注持续集成。李建业还经常受邀出席一些技术大会并发表主题演讲,比如 InfoQ,Ruby 大会等。

丁雪丰:一线攻城师一枚,InfoQ 中文站小编,常年混迹于各种社区,业余时间写作翻译、汉化软件,《RESTfulWebServices Cookbook 中文版》、《Spring 攻略》等多部图书的译者。

马钧:全端程序员,15 年以上多种程序编程经验,爱好开源软件,积极参加各类社区活动,《REST 实战》等技术图书的译者。

评论

发布