者:人月神话,新浪博客同名
简介:多年SOA规划建设,私有云PaaS平台架构设计经验,长期从事一线项目实践
在前面关于微服务方面的文章里面提到,对于多个微服务模块间往往都是以轻量的Http Rest API接口方式进行集成和协同。那么对于API接口的设计,接口服务的注册接入,后续的治理管控就是整个微服务架构里面不可或缺的内容。因此今天将对这部分内容重新做下总结。
对于HTTP Rest接口的设计,网上已经有很多文章都有详细的阐述,今天再重新整理下这里面的一些重点,大家都清楚Rest接口是面向资源的接口设计方法,而且基于原生的Http协议,因此里面就有两个最关键的点,一个就是对资源的理解,一个就是对操作的理解。
图片来源网络
什么是RESTful架构,重要的几点如下:
对资源的理解
就是我们平常上网访问的一张图片、一个文档、一个视频等。这些资源我们通过URI来定位,也就是一个URI表示一个资源。资源是做一个具体的实体信息,它可以有多种的展现方式。而把实体展现出来就是表现层,例如一个txt文本信息,它可以输出成html、json、xml等格式,一个图片他可以jpg、png等方式展现,这个就是表现层的意思。
URI确定一个资源,但是如何确定它的具体表现形式呢?应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对”表现层”的描述。
对操作的理解
客户端能通知服务器端的手段,只能是HTTP协议。
具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
对于资源的任何操作,都应该映射到HTTP的几个有限的方法(常用的有GET/POST/PUT/DELETE 四个方法,还有不常用的PATCH/HEAD/OPTIONS方法)上面。所以RESTful API建模的过程,可以看作是具有统一接口约束的面向对象建模过程。
按照HTTP协议的规定,GET方法是安全且幂等的,POST方法是既不安全也不幂等的(可以用来作为所有写操作的通配方法),PUT、 DELETE方法都是不安全但幂等的。将对资源的操作合理映射到这四个方法上面,既不过度使用某个方法(例如过度使用GET方法或POST方法),也不添 加过多的操作以至于HTTP的四个方法不够用。
面向资源的设计
图片来源网络
注意传统的接口设计一般是面向方法和操作的,即一般都是动宾结构为主的方法操作。比如查询用户信息接口,更新供应商状态接口,删除人员信息接口等等。而对于面向资源的设计,一个大的转变就是首先要识别和定义资源,再来对资源的Http各种操作方法进行定义。
资源可以理解为对象,更多的是数据模型,这种数据模型本身就是有层次结构,本身是可以嵌套或递进的。比如你可以将订单理解为一个资源,那么这个订单资源本身是一个层次化的结构对象,包括了订单头信息,也包括了订单明细信息。我们可以对这个层次化的资源结构进行详细定义。
在数据库设计的时候,经常会出现视图的设计,或者说表之间的关联,而这种视图本身就不是一种资源实体,但是在进行资源定义的时候,我们可以定义这种层次化的对象结构。
举个例子来说,在对订单这种资源对象进行定义的时候,在订单明细中传统的视图中会关联订单明细表和物料表形成订单行ID,物料编码ID,物料名称,类型,订购数量的关联视图信息。但是在资源定义的时候,我们仍然只需要定义订单行信息,但是订单行信息中出现了物料,我们形成一个对物料资源信息的Ref引用,而获取到完整的物料属性信息。
当然资源本身也有关联关系,我们可以定义这种关联关系来获取到关联资源的信息,比如某个用户发布的评论信息,我们可以先定位到该用户资源,然后再定位到该用户资源对应的评论资源。
具体可以参考:
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
http://blog.51cto.com/xxdeelon/2083707
为了使 API 看上去简单明了,可读性强,我们一般使用名词,而不是动词来命名这些资源。比如下面这些都是糟糕的设计,比如下面的动宾结构的方法名作为资源:
之所以糟糕,不仅仅是它们显得拖沓冗长,最重要的是,使用这样的风格和名字没有固定的形式,不同的开发者往往需要阅读你的文档才能开始使用,也没有充分利用HTTP Method,何况使用自己的动词可能会产生和HTTP Method冲突的情况。使用 REST 风格的优秀设计应该像下面这些:
这些API所有的操作只有一个节点 /users,显得简洁明了,如果熟悉 HTTP Method 的开发者,一眼看上去就能猜到应该如何使用。
注:在现在进行Http Rest API接口设计的时候,实际上往往并没有遵循面向资源设计的思路,仍然是参考上面的操作+数据的设计方法,比如有些公司的设计规范往往还是全部约定为使用POST接口设计,这个并不能说就一定不合理。这种情况更多就是使用Http API而已。
您可能已经注意到了,以上 API 中资源命名都使用了复数的形式。这是一个约定,它可以省去设计时考虑数据具体细节的麻烦(数据是复数还是单数?)。现在大很多常见的系统都使用了复数形式。比如Twitter 的 REST APIs 和 Facebook 的 Graph API 基本都是复数形式。
然而实际系统一般都不可能只有单一资源,资源和资源之间有各种关系是很正常的情况,那么如何设计存在关联资源(数据)的API呢?如果要设计一个资源拥有另外一个资源的情况的API,例如,设计一个包含用户(users)和用户的评论(comments)的 API 可以采用这样的形式:
当然,如果一个资源并不依附其它资源而可以独立存在,是没有必要这样设计的,完全可以使用和 users 一样的形式提供,如果要查询其中的关系,可以使用其它资源作为 ID 的形式来过滤。
例如 /comments?user_id=1234。关于这点详细内容可以参见下面的第三条“优雅的设计条件过滤,排序,搜索和限制返回数据的参数形式”。
对于Http Rest接口设计规范,可参考
https://blog.csdn.net/zghwaicsdn/article/details/53788535
https://blog.csdn.net/zl1zl2zl3/article/details/73867113
https://blog.csdn.net/yyjava/article/details/81626991
https://www.jianshu.com/p/00d1ab8cc073
http://wangwei.info/about-rest-api/
资源定义格式:
URI=scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]
URL是URI的一个子集(一种具体实现),对于REST API来说一个资源一般对应一个唯一的URI(URL)。在URI的设计中,我们会遵循一些规则,使接口看起透明易读,方便使用者调用。
"/"分隔符一般用来对资源层级的划分
例如 http://api.canvas.restapi.org/shapes/polygons/quadrilaterals/squares
对于REST API来说,"/"只是一个分隔符,并无其他含义。为了避免混淆,"/"不应该出现在URL的末尾。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
在我们在实施过程中如何通过一些工具支撑来进行接口设计的规范化和标准化问题。大家都知道,在采用传统的SOAP WebService服务接口的时候,由于SOAP WS服务本身有WSDL和XSD文件的强接口契约规范格式要求,因此很容易基于从上向下,规范先行的思路来进行服务实施流程管控。即:
可以看到传统的WS服务实施下,我们可以通过规范和契约先行的方式很好的控制接口的标准化和一致性,同时大家遵循完全相同的一套WSDL文件进行接口的提供或开发,基本不存在接口实现中数据结构不一致的问题。
在采用RestAPI接口进行设计的时候,实际上仍然有一个关键点,就是设计要先行,先设计,然后再进行开发和测试,同时设计文件能给标准化和结构化,通过设计文件来生成相应的客户端和服务端代码框架,通过设计文件来生成相应的测试用例,通过设计文件来生成对应的API接口设计文档。
设计先行,是Http Rest接口服务实施中的一个关键点。
1. 设计工具的选择问题
当前两者主流的设计规范和建模语言,一个是RAML,一个是WADL。
RAML(RESTful API Modeling Language) 即 RESTful API 建模语言)是对 RESTful API的一种简单和直接的描述。它是一种让人们易于阅读并且能让机器对特定的文档能解析的语言。
RAML 是基于 YAML,能帮助设计 RESTful API 和鼓励对API的发掘和重用,依靠标准和最佳实践从而编写更高质量的API。通过RAML定义,因为机器能够看得懂,所以可以衍生出一些附加的功能服务,像是解析并自动生成对应的客户端调用代码、服务端代码 结构, API说明文档。
https://www.w3cschool.cn/web_api_next/qw2zpozt.html
https://raml.org/projects
https://www.cnblogs.com/softidea/p/5728952.html
WADL(Web Application Description Language)是一种用于描述web应用对外提供接口语言,它使用XML来表示。其中主要包括每个资源的定位(URI)、请求类型(GET、POST等)、请求参数类型(路径参数、query参数等)、响应值和响应类型等一系列内容。
根据这个XML,可以直接读取到系统提供的所有REST API,并能够进行调用。由于WADL使用XML描述,对于开发者来说,可以通过标准的xslt进行转换,将机器读取的XML转换成便于人读取的HTML,进而行成API文档。
https://coolex.info/blog/547.html
http://blog.chinaunix.net/uid-9789791-id-1997442.html
http://www.doc88.com/p-6661149184381.html
注:对于两种接口规范如何选择的问题?个人认为如果我们需要一套接口规范同时兼容传统的SAOP WebService和Http Rest API,那么采用WADL更好。如果已经是全新Http Rest接口设计,往往当前RAML更加主流。
主流的Swagger设计工具
对于设计工具,这里只谈下Swagger设计工具,这个工具最大的好处就是只需要通过编辑器进行Rest接口设计文件的定义,然后可以自动生成测试框架,自动生成客户端和服务端多种语言下的开发框架,生成API接口设计文档,这个工具本身设计完成内容还可以导出为YAML文件或Json文件,也支撑文件导入。
https://swagger.io/tools/
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。Swagger的目标是对REST API定义一个标准的和语言无关的接口,可让人和计算机无需访问源码、文档或网络流量监测就可以发现和理解服务的能力。
当通过Swagger进行正确定义,用户可以理解远程服务并使用最少实现逻辑与远程服务进行交互。与为底层编程所实现的接口类似,Swagger消除了调用服务时可能会有的猜测。Swagger是一组开源项目,其中主要要项目如下:
Swagger API Spec包含以下部分内容,详细参考:
https://www.cnblogs.com/jpfss/p/8072443.html
注意通过Swagger-Editor编辑器可以完成整个接口的定义和接口设计工作,同时一方面用于客户端和服务端代码框架的生成,一方面用于帮助文档的生成。但是Swagger对于帮助文档的生成需要在代码框架里面增加注解,相对来说注解编写的工作量较大。这也是网上很多提到的文档生成方面Swagger往往并不是最佳选择。
原有的接口服务规范编写思路改进
对于原来的接口服务规范编写思路,可以看到几个改进或优化思路。
1. 还是保留先编写Word格式的接口服务规范,然后再将Word接口服务规范转为标准的YAML或WADL接口规范定义文件,同时再生产客户端或服务端的代码框架文件。
2. 抛弃Word编写规范模式,直接在类似Swagger编辑器中对接口文件进行设计,设计完成后发出文件进行评审和检查,没问题后由集成平台归档并导入管控生产服务规范,同时生成客户端和服务端代码框架一同下发。
现在发现的一个问题不好解决点在于,在进行接口文件设计的时候能否直接增加字段和数据项的中文描述和业务备注说明,该信息最终体现到生成的文档上。或者说我们需要通过YAML或Json文件,来逆向生成Word文档,然后Word文档再下发给业务系统补充中文描述信息。
API文档的生成
如果采用Swagger工具来进行API接口服务的设计,可以看到在编辑器里面,设计过程和文档生成过程基本是同步的,即一个API接口设计好后,文档基本就已经生成完成并可用。当前唯一发现的问题就是没有想过的中文描述,如果需要中文信息,还需要在代码框架里面增加注解,这个过程来说就想到繁琐了。
通过Swagger注解生成文档:
https://www.jianshu.com/p/0438034ee55f
SpringBoot项目生成RESTfull API的文档,第二篇给出SpringBoot和Swagger整合思路
https://www.jianshu.com/p/af7a6f29bf4f
http://blog.didispace.com/springbootswagger2/
https://gitee.com/didispace/SpringBoot-Learning
Wisdom REST Client
Wisdom RESTClient supports automated testing and automatically generating RESTful API document based on history cases. Wisdom RESTClient可以自动化测试RESTful API 接口,同时,基于测试过的历史数据,可以自动生成API文档。
工具地址:https://github.com/Wisdom-Projects/rest-client
这个工具是偏测试后的文档生成,最终生成的文档展现和Swagger展现效果类似。
Api2Doc文档生成工具
Api2Doc 专注于 Restful API 文档的自动生成,它的原理与 Swagger2 是类似的,都是通过反射,分析 Controller 中的信息生成文档,但它要比 Swagger2 好很多。最大的不同是: Api2Doc 比 Swagger2 要少写很多代码。
第一是对应注解过程本身更加简单,其次就是可以自动分析类引用,将类定义中关于数据项的注释信息引用到最终生成的文档中,这个功能就相当好用了,很好的解决了我们前面提到的数据项中文描述的问题。
参考:
http://blog.51cto.com/13613194/2090764
https://www.cnblogs.com/yoyotl/p/6376624.html
使用Asciidoc代替Markdown和Word撰写开发文档
Asciidoc是另外一个可以用于HttpRest接口文档生成的工具。该工具简洁而不简陋的语法,它专门为编写书籍而生,在语法的支持上很到位,但不像Word那样可以随性,可以让你的文档更统一美观。AsciidocFX工具开源跨平台,使用体验很不错,更可以导出HTML、PDF、EBook等格式
具体参考:
https://my.oschina.net/gudaoxuri/blog/524132
http://houqp.github.io/wbwa/wbwa.html
感觉该工具完全可以用于帮助手册的制作。常用的列表,段落,超链接,图片,表格,代码等能力都支持。
对于API管理平台参考
https://www.eolinker.com/#/
对于Eolinker基本提供了一套完整的API全生命周期管理方案。无论您使用什么语言开发,无论是 HTTPS、Websocket、TCP、UDP 等协议,还是 Restful、SOAP、WebService 等规范,Eolinker 都可以帮您统一规范地管理起来,并提供强大的文档管理、协作、测试、分享功能。
由于我们原来重点是实施传统ESB服务总线项目,以SOAP WS服务接入为主,并形成了完整的SOA治理管控平台,实现服务全生命周期管理和运维监控。而对于Http Rest接口服务的注册接入和后续管控,要完全重新实现一套面向Http Rest接口的平台并不太现实。
因此需要基于当前的SOA管控平台来思考如何平滑扩展对Http Rest接口服务的支撑能力。
首先我们要意识到,ESB总线和微服务网关是可以并行存在的。在一个集团企业内部,有些业务域已经完全实施微服务架构转型和改造,而有些业务系统仍然采用了传统架构,企业在朝微服务架构转型过程中实际上是一个逐渐过渡的过程。在传统架构支撑中我们搭建了ESB企业服务总线实现接口服务集成和统一管控,而在微服务架构支撑中,我们如何支持?
这里的一个关键思路和边界就是,对于某个业务域完全实施了微服务架构转型,比如一个大供应链系统的建设,里面涉及到招投标,采购,物流,库存等多个微服务模块,都按照微服务架构进行设计,开发和集成。那么在这个业务域内部就需要有微服务注册中心,同时又一个微服务网关统一提供对外能力。
那么企业原来已有的全局ESB服务总线只是和该业务域提供的微服务网关进行对接。这个有点类似于两级ESB集成模式,即供应链内部微服务模块间的集成不走ESB总线,在内部通过注册中心或网关完成即可。
其次ESB总线虽然比较重但是不代表性能弱,我们经常会遇到业务系统提出的问题,就是走ESB服务总线集成是否太重,而应该改为轻量的API网关类产品。在这里我们要提出一个关键点,就是ESB总线虽然说是一个重型的产品,但是这种重更多是指的其对底层资源和中间件的要求比较高,并不是说重量级的产品性能就差。
对于ESB这种重型产品,底层引擎更加强大,当你只启用简单的Http服务代理,安全等功能的时候,其性能,可靠性和稳定性反而是远远高于我们经常说的各种开源的API网关类产品的。
举个例子来说,就拿各种重量级的V6大型越野车来说,其重点是油耗高,刚开始的加速弱点,当时真正加速起来后的性能,通过性和稳定性远超各种一般的轿车。而且越野车不是说只跑山路烂路有优势,就是其跑高速路带来的性能你也不上。
微服务网关或API网关,我们看到,只要是这种网关类产品,就一定需要提供服务代理能力来保证服务位置透明,但是一提供这个能力,那么所有的消息流就一定会通过网关来承载,因此API网关本身受到的性能压力,高可用性压力是很大的。也对产品本身也有更高的要求,而对于大型ESB总线在这块往往已经得到充分验证。
也就是说,要追求性能,我们不要用ESB总线里面类似协议转换,数据映射,适配等复杂消耗性能的功能,而将ESB总线当作网关类产品来用就可以了,这样ESB总线完全可以提供类似API网关应该有的性能。
最后,实现对Http Rest接口服务的注册和接入并不一定说要完全遵循面向资源编程的思路。谈Http Rest接口设计的时候,一定会提到面向资源的编程思路,包括资源的识别和定义,基于资源的各种标准Http操作。但是要注意到,在一个微服务架构内部,我们完全可以按这个思路来执行和推广。但是在涉及到跨域的基础,微服务网关和传统业务系统,ESB集成的时候。
这个接口设计是否一定遵循Http Rest面向资源的设计规范并不那么重要。
也就是说,传统ESB在接入Http Rest接口的时候,我们完全可以按传统的接口设计方法进行设计,比如全部走POST接口设计。只是传统协议走了标准的Http接口协议,传输内容走了更加轻量的Json格式而已。只有把这点想明白,才能给继续思考我们当前的SOA管控平台,如何能给平滑的实现对Http Rest接口服务接入的支撑。
基于前面的思路,对于当前的SOA管控平台,需要考虑如何平滑的支撑Http Rest接口服务。
1. 对于新的Http Rest接口的注册和接入
对于当前已有的基于SOAP WS的服务规范定义文档,最好的方式就是做到SOAP和REST接口都能给通用,虽然这样不符合Rest面向资源的要求,也丧失了Rest接口本身的一些灵活性,但是可提升对服务规范的标准化管理。在这种模式下,对于服务规范涉及到的变化估计在于:
也就是说,我们完全可以做到一套服务规范来支撑SOAP和Rest接口,毕竟在做跨系统间接口服务集成的时候,这里的接口更多都是粗粒度的服务定义,并不会涉及到细粒度的数据表或对象的所有CRUD操作。跨系统间的接口更多的是满足跨业务协同和数据集成需要即可,是横向系统和系统之间的接口,而不是类似单个业务系统展现层和逻辑层到DB层的接口。
对于Http Rest接口,传递的数据要支持XML和Json两种格式,这个也需要提前进行约定和配置。而为了兼容当前的服务规范格式,可以看到这样转过了的Rest接口方法全部为Post接口,同时接口服务名即为资源对象名,同时在接口调用的时候传递的是一个我完整的数据集合对象。这和我们前面讲Rest接口的设计和调用是有区别的。
规范定义讲清楚后,整个过程就简单了,原来已有的直接在界面上定义服务规范或者通过Word文档导入服务规范功能基本都可以保留,做少量变更修改即可。导入的规范本身也可以进一步导出或直接在线查看。
对于Rest接口服务,我们仍然可以遵循一样的契约先行和自顶向下设计的思路进行。
规范清楚后,接着还是需要导出标准的契约格式文档,而这里建议还是采用WADL,基于Rest接口的服务定义语言,有强契约格式要求。而对于RAML,更多是Rest接口设计建模的语言,类似于通过RAML来写文档。如果我们是以Word服务规范文档格式为主,实际上是没必要再用RAML。但是可以考虑服务规范导出RAML语言定义。
RAML(RESTful API Modeling Language 即 RESTful API 建模语言)是对 RESTful API的一种简单和直接的描述。它是一种让人们易于阅读并且能让机器对特定的文档能解析的语言。这样也方便我们后面基于RAML来生成API定义文档。
在规范导出为WADL设计文件后,那么就有严格契约格式要求,业务系统可以基于该WADL定义文件,通过工具自动生成客户端和服务端的代码开发框架,然后再填充业务规则和逻辑,这个过程和我们标准的SOAP WS接口服务的设计和开发是一致的,并没有太多的区别。
在服务提供端按WADL要求开发后Rest接口服务后,由ESB进行服务代理封装接入,这个需要单独设计功能支持,即需要采用单独定制的OSB服务封装模板。在服务代理接入后,我们原来对于SOAP接口的安全,日志等控制能力全部都需要保留和平滑支持。
服务代理接入后,仍然自动化部署,部署完成后给出代理封装后的访问地址。
对于服务调用访问,本身也会产生日志记录,对于已有的服务运行日志监控,服务异常日志监控等功能,都需要完全平滑的支撑Http Rest接口服务。对于原有的日志元数据配置表,服务日志运行实例表等也保留一套。
2. 对于SOAP和Rest接口本身的转换映射问题
当我们采用同一套规范体系的时候,我们就很容易去实现两种类型接口的转换和映射。
业务系统如果提供的SOAP WS接口服务,我们需要单独提供一个功能,将该SOAP WS服务发布为Http Rest接口服务,同时在发布的时候可以选择是采用XML格式,还是Json格式进行发布。
这样好处就是原来业务系统以及提供了SOAP接口,但是当我们在对接一个微服务架构体系的时候,消费端为了自身的标准化需要消费Rest接口服务,那么由ESB来完成这种转换映射即可,原有的业务系统服务提供方不再需要做任何服务设计变更和代码修改。
当然,如果业务系统本身提供的Http Rest接口服务,为了对已有的历史系统进行支撑,我们也可以将Http Rest接口服务转化为SOAP WS服务再进行暴露。在转换的时候新数据格式只能够是XML格式。
3. 对于消息发布订阅,消息中间件的支持
对于消息发布订阅,我们采用Weblogic JMS消息中间件,但是将JMS能力适配后发布为SOAP WS接口服务。在Http Rest接口服务实施过程中,我们需要支撑将JMS适配后能力直接发布为Rest接口服务。
当然为了简化,该功能非必须,即我们可以考虑在ESB接口上做两次代理,即将ESB已经暴露的SOAP WS消息分发服务接口,通过Http Rest接口服务转换功能直接进行转换后二次发布。
对于JMS消息订阅端,本身走Java API接口进行消息消费,本身和服务采用SOAP还是Rest没有太大关系。
4. 对于Http Rest接口的纯代理接入
对于业务系统已有的HttpRest接口,如果不希望按严格的服务规范设计和契约先行的思路进行变更,我们也可以直接对已有的Rest接口进行纯代理接入,即ESB做完全的代理转发,不做任何的安全控制,输入内容消息头的解析,数据映射转换等。对于这类Rest接口服务,ESB总线只起代理转发作用。
对于ESB服务总线和API网关产品来说,在前面很早的博客文章自己就谈到过,整体思路是底层引擎是两套,即一个是偏重的ESB总线引擎,一个是API网关引擎,但是对于SOA治理管控和运营开放则是整合为一套。一个是SOA运维监控平台是统一的一套,一个是能力开放平台也统一为一套。
但是我们看到虽然ESB总线是一个偏重的引擎,但是我们不启用其复杂的协议转换,数据映射,服务编排等功能的时候仍然可以做为要给轻量的SOA总线来使用。而且我们看到另外一个场景,即企业很多时候不会很快就完成一个微服务架构化的转型,始终是存在传统的遗留系统。
因此集成问题和场景本身是很复杂的,即使整个集成趋势是Http Rest接口集成和API网关集成为主,但是你还是得兼容传统观的WS服务集成和简单的协议转换能力。
实际上对于ESB总线来说本身就是支持Http Rest接口服务得注册和接入的。因此实际上对ESB服务总线和API网关引擎存在两种思路可以选择。
对于第二种方式相对来说并不会很复杂,也容易实施,即通过对ESB服务总线的升级来完成对ESB总线+API网关两方面能力的完全支撑。你可以说卖的是ESB服务总线,但是完全兼容适配API网关所有能力。
基于上面这个思路,我们需要做的主要包括
基于以上关键点进行进一步的优化和完善后,即能够为企业提供一套完整的SOA服务总线产品,同时支撑传统的ESB服务总线能力,又对Http Rest API接口的接入,注册和管控方面能力得到全面增强。
欢迎关注@人月聊IT 分享SOA,微服务,DevOps平台规划和建设。
篇 文章主要是借鉴他人,但是自己很想总结出一套规范,以供向我这样的新手使用,用来规范代码,如果有什么好的提议,请不吝赐教,本篇文章长期更新!
一、重要概念:
REST,即Representational State Transfer的缩写。我对这个词组的翻译是"表现层状态转化"。
Resource(资源) :对象的单个实例。 例如,一只动物。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
集合:对象的集合。 例如,动物。
第三方:使用我们接口的开发者
表现层(Representation)
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
状态转化(State Transfer)
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
二、REST接口规范
1、动作
GET (SELECT):从服务器检索特定资源,或资源列表。POST (CREATE):在服务器上创建一个新的资源。PUT (UPDATE):更新服务器上的资源,提供整个资源。PATCH (UPDATE):更新服务器上的资源,仅提供更改的属性。DELETE (DELETE):从服务器删除资源。
首先是四个半种动作:post、delete、put/patch、get因为put/patch只能算作一类,所以将patch归为半个。
另外还有有两个较少知名的HTTP动词:HEAD - 检索有关资源的元数据,例如数据的哈希或上次更新时间。OPTIONS - 检索关于客户端被允许对资源做什么的信息。
2、路径(接口命名)
路径又称"终点"(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
举例来说,有一个API提供动物园(zoo)的信息,还包括各种动物和雇员的信息,则它的路径应该设计成下面这样。
接口尽量使用名词,禁止使用动词,下面是一些例子。
GET /zoos:列出所有动物园 POST /zoos:新建一个动物园 GET /zoos/ID:获取某个指定动物园的信息 PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息) PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息) DELETE /zoos/ID:删除某个动物园 GET /zoos/ID/animals:列出某个指定动物园的所有动物 DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
反例:
/getAllCars /createNewCar /deleteAllRedCars
再比如,某个URI是/posts/show/1,其中show是动词,这个URI就设计错了,正确的写法应该是/posts/1,然后用GET方法表示show。
如果某些动作是HTTP动词表示不了的,你就应该把动作做成一种资源。比如网上汇款,从账户1向账户2汇款500元,错误的URI是:
POST /accounts/1/transfer/500/to/2
正确的写法是把动词transfer改成名词transaction,资源不能是动词,但是可以是一种服务:
POST /transaction HTTP/1.1
Host: 127.0.0.1
from=1&to=2&amount=500.00
理清资源的层次结构,比如业务针对的范围是学校,那么学校会是一级资源(/school),老师(/school/teachers),学生(/school/students)就是二级资源。
3、版本(Versioning)
应该将API的版本号放入URL。如:
https://api.example.com/v1/
另一种做法是,将版本号放在HTTP头信息中,但不如放入URL方便和直观。Github采用这种做法。
4、过滤信息(Filtering)
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。下面是一些常见的参数。
?limit=10:指定返回记录的数量 ?offset=10:指定返回记录的开始位置。 ?page_number=2&page_size=100:指定第几页,以及每页的记录数。 ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。 ?animal_type_id=1:指定筛选条件 参数的设计允许存在冗余,即允许API路径和URL参数偶尔有重复。比如, GET /zoo/ID/animals 与 GET /animals?zoo_id=ID 的含义是相同的。
5、状态码(Status Codes)
状态码范围
1xx 信息,请求收到,继续处理。范围保留用于底层HTTP的东西,你很可能永远也用不到。 2xx 成功,行为被成功地接受、理解和采纳 3xx 重定向,为了完成请求,必须进一步执行的动作 4xx 客户端错误,请求包含语法错误或者请求无法实现。范围保留用于响应客户端做出的错误,例如。他们提供不良数据或要求不存在的东西。这些请求应该是幂等的,而不是更改服务器的状态。 5xx 范围的状态码是保留给服务器端错误用的。这些错误常常是从底层的函数抛出来的,甚至 开发人员也通常没法处理,发送这类状态码的目的以确保客户端获得某种响应。 当收到5xx响应时,客户端不可能知道服务器的状态,所以这类状态码是要尽可能的避免。
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务) 204 NO CONTENT - [DELETE]:用户删除数据成功。 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 502 网关错误 503 Service Unavailable 504 网关超时
参考资料:
RESTful API 设计指南--阮一峰:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
首先 RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。
REST这个词,是 Roy Thomas Fielding 在他2000年的博士论文中提出的。
REST,即 Representational State Transfer 的缩写,如果一个架构符合REST原则,就称它为RESTful架构。
要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。
REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。
所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。
所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。
"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。
举例说明:对用户表的的操作,增删改查平衡车那个都是这么写:
每种业务都有自己的路径,增删改查前端会看到四种不同的路径,代码整体看起来比较冗余
很明显的区别,请求路径统一,通过请求方式区分获取、删除、修改、保存 等动作,清爽多了
既然这么简洁,我们实操一下吧
第1步:application.propertities 文件配置 spring.mvc.hiddenmethod.filter.enable=true,开启表单的REST风格支持(这个源码分析部分会讲到)
第2部:编写一个前端页面实测一下 REST 风格的提交:
注意下页面的 hidden隐藏域参数细节(后面源码部分会提到)
点击四个按钮,发现跳转正确!
他是怎么跳转的,看源码分析
SpringBoot的处理思路是:既然浏览器天然只能发送 GET 和 POST 请求,那可以利用前后的参数配合,根据约定好的参数,把POST或者GET 定向地转成 PUT 或 DELETE 请求
实现对REST 请求风格的支持主要得益于HiddenMethodFilter 类,它同样是在WebMvcAutoConfiguration 这个自动配置类中在项目启动时自动注入的。
着重看一下 HiddenHttpMethodFilter ,顾名思义 隐藏HTTP方法过滤器,它的类注释翻译完粘贴过来:
由于浏览器目前仅支持 GET 和 POST,因此一种常用技术(例如 Prototype 库使用的技术)是使用带有附加隐藏表单字段 ( _method ) 的普通 POST 来传递“真正的”HTTP 方法。 此过滤器读取该参数并相应地更改HttpServletRequestWrapper.getMethod()返回值。 只允许"PUT" 、 "DELETE"和"PATCH" HTTP 方法。
请求参数的名称默认为_method ,但可以通过methodParam属性进行调整。
看下 HiddenHttpMethodFilter 的核心方法 doFilterInternal() , 他是如何吧我们的隐藏表单解析的
如何获取?
转发分享此文,后台私信小编:“666”即可获取。(注:转发分享,感谢大家)
*请认真填写需求信息,我们会在24小时内与您取得联系。