关于restful:2023再谈RESTful-和-GraphQL

前段时间组内搞代码检视,常常能看到一些 “挂着 RESTful 羊头,卖的却是 GraphQL 狗肉”的 API 设计。举个例子,如果后盾有两种资源用户 User 和 群组 Group ,依照RESTful的标准,他们设计以下API端点: # 获取用户列表GET /users# 获取指定用户GET /user/{id}# 创立用户POST /users# 批改用户PUT /user/{id}# 删除用户DELETE /user/{id}# 获取群组列表GET /groups# 获取指定群组GET /group/{id}# 创立群组POST /groups# 批改群组PUT /group/{id}# 遣散群组DELETE /group/{id}咋一看没啥问题,可是到了前面,要实现 “用户退出群组” 和 “用户退出群组” 两个个性时,他们给出的API设计: # 用户退出群组PATCH /group/{id}{ "addMembers": ["userID"]}# 用户退出群组PATCH /group/{id}{ "deleteMembers": ["userID"]}我当场就蚌埠住了,这哪里是 RESTful 格调,这不就是他们老大深痛恶绝的 GraphQL ?眼看我就要当场发飙,有个新手连忙进去圆场,他给出以下API设计: # 用户退出群组PATCH /user/{id}/group/{groupID}# 用户退出群组DELETE /user/{id}/group/{groupID}这个设计看起来是解决方才的问题,但实际上只是自欺欺人。我接着诘问: “管理员邀请用户退出群组” 和 “管理员将用户踢出群组” 要怎么设计呢?那个新手依瓢画葫芦给出以下设计: # 管理员邀请用户退出群组PATCH /group/{id}/user/{userID}# 管理员将用户踢出群组DELETE /group/{id}/user/{userID}眼看他们还不觉悟,我就接着诘问:“用户退出群组须要管理员批准” 和 “管理员邀请用户退出群组须要用户批准” 又怎么实现呢?这下子新手也没辙,只能说这块的代码须要从新设计。 我让他们把原来的设计文档从新拿进去检视,而后发现很多中央都不符合规范,连根本的实体关系图ER都没有!我忍不住吐槽:怪不得当年我一个人拿着不到一半的工资,连前后端一起搞,效率却比这帮985/211的高材生/海归还要高,而他们连一个最根本的CURD都要一直地返工! ...

April 10, 2023 · 1 min · jiezi

关于restful:开源API网关APINTOIP黑白名单

公司要求配置IP黑白名单,看了一下Apinto官网介绍,服务治理的拜访策略可配置IP黑白名单。废话不多说,间接上演示。Apinto拜访策略原理:配置筛选条件,用来筛选出符合条件的API申请,即筛选流量,依照配置拜访规定执行容许拜访或回绝拜访失效范畴。配置前置条件,用一个IP为192.168.160.1只能申请的门路是/api/test这个接口,申请其余门路的API报错,不容许拜访。 新建拜访策略筛选条件,属性名称选IP,那么属性值能够是单个IP,也能够是一组IP,把这样的IP浏览筛选进去。当拜访规定配置的是容许时,则这一组IP能够拜访失效范畴的条件,不是这一组IP的申请将不会容许放行。 下面动图中,把一个IP为192.168.160.1的流量筛选进去,容许拜访申请的门路是/api/test这个接口,其余门路的接口是不会被容许拜访的。 公布拜访策略测试后果通过测试后果,咱们能够看到,拜访 /api/test 时会返回响应后果。然而,当拜访 /api/test1 或 /api/test2 时,会失去拜访 {IP} 的申请被回绝的提醒。 总结:当配置的策略筛选条件是一组IP,拜访规定是容许时,且失效范畴不增加任何条件,则这组IP在网关层面是IP白名单,反之亦然。总之这个拜访策略性能切实是太强大了,精准筛选流量执行容许或回绝的失效范畴。好货色必须关注,好了,省得大家去搜,间接提供github地址。 开源地址:https://github.com/eolinker/apinto

March 26, 2023 · 1 min · jiezi

关于restful:RESTful-API-为何成为顶流-API-架构风格

作者孙毅,API7.ai 技术工程师,Apache APISIX Committer万物互联的世界充斥着各式各样的 API ,如何兼顾标准 API 至关重要。RESTful API 是目前世界上最风行的 API 架构格调之一,它能够帮忙你实现客户端与服务端关注点拆散,让前后端各自迭代,晋升管理效率;其无状态的个性能够让利用更容易扩大,更容易的实现缓存策略从而晋升零碎性能和用户体验。本文将介绍什么是 RESTful API 以及咱们如何应用它。 首先,抛开 API 这个概念,咱们来聊聊在生活中如何传递信息。 当你在商店将钱拿给老板通知他你须要买电池,老板在收到钱后在货架上找到电池并递给你。一个买电池的交易顺利完成。 计算机软件则通过 API 来实现信息的传递,先来看维基百科定义: An application programming interface (API) is a way for two or more computer programs to communicate with each other. It is a type of software interface, offering a service to other pieces of software.软件 A 通过 API 向软件 B 发动申请,软件 B 在查问本人资源后将申请的响应内容通过 API 返回 A。 软件 A 通过 API 向软件 B 发动申请就好比你跟老板说你须要电池,软件 B 在将数据返回给软件 A 就好比老板找到电池后将电池给你。 ...

February 10, 2023 · 3 min · jiezi

关于restful:独立产品灵感周刊-DecoHack-035-YouTube-设计改版了

本周刊记录乏味好玩的独立产品设计开发相干内容,每周公布,往期内容同样精彩,感兴趣的搭档能够点击订阅我的周刊。为保障每期都能收到,倡议邮件订阅。欢送通过 Twitter 私信举荐或投稿。1. Darkmodes - 这个网站收集了很多暗黑模式做的很好的网站。 webroll - 这个网站能够随机去一个独特乏味的网站。之前周刊也举荐过几个。Radio.garden - 这个网站能够听到全世界各地的电台,这是我发现做的最好的一个电台网站了,很全。就跟玩 google earth 一样轻易找找,还有另外一个 internet-radio 也能够找到一些。fReddit - 这个网站依据你的抉择举荐一些 Reddit 板块,比官网的更直观一点。Reddit 下面还是有十分多有意思的内容的。找找喜爱的板块去看看。Sirin Audiobook Player - 这是一个有声书本地播放器,当初还是有很多人有这个需要的,这个软件是一个很洁净的工具,反对播放很多种音频格式,反对下载种子文件,还能够剖析有声读物的章节和封面。内购产品,也很适宜独立开发者来做,也算是比拟小众。threadvideos - 这个产品想法很不错,能够用来把 Reddit 或者 Twitter 的帖子内容制作成视频,用来公布到 TikTok, YouTube, Facebook, Instagram 这些平台。是一个很有用的小工具。不错的独立开发我的项目。当初还没有上线,能够去官网看看操作介绍。开源我的项目1. cal.com - 开源的日程安排工具,设计的很不错,开源的。11 个让您的网站怀才不遇的导航组件 - 前端的代码片段,能够看看有些挺有创意。轻易看看1. Sam Ruston - 这是 Android 开发者 @Sam_Ruston 的官网,他卡发了很多有意思的 APP,能够去他的 Google Play 主页看看。设计的也挺简洁难看。作者还取得过谷歌的 Material Design award .YouTube 的更新外观设计 - 刚看了一下 YouTube 设计改版了,昨天更新的,界面更好看了,快去看看,手机端和 Web 端都有变动,微光成果看上去很难受。网页变大圆角。更多内容能够订阅我的周刊:竹白订阅|官网|RSS订阅 |Telegram频道|Twitter另外,往期举荐的产品根本没有时效性,感兴趣的搭档能够去看看我整顿的往期产品数据库,能够筛选/分类/标签/搜寻我举荐过的所有软件,更多介绍请看 会员打算。

October 25, 2022 · 1 min · jiezi

关于restful:13个构建RESTful-API的最佳实践

前言Facebook、GitHub、Google和其余许多巨头都须要一种办法来服务和生产数据。在明天的开发环境中,RESTful API依然是服务和生产数据的最佳抉择之一。 但你是否思考过学习行业标准?设计一个RESTful API的最佳实际是什么?实践上来说,任何人都能够在5分钟内疾速启动一个数据API。无论是Node.js、Golang,还是Python。 咱们将摸索构建RESTful API时应该思考的13个最佳实际。 最佳实际本文为你提供了13个可操作的最佳实际清单。让咱们一起来摸索吧! 正确应用HTTP办法咱们曾经探讨了你能够用来批改资源的可能的HTTP办法:GET,POST,PUT,PATCH,和 DELETE。 然而,许多开发者往往会滥用GET和POST,或者PUT和PATCH。通常状况下,咱们看到开发者应用POST申请来检索数据。此外,咱们看到开发者应用PUT申请来替换资源,而他们只想更新该资源的一个字段。 确保应用正确的HTTP办法。如若不如此做,将为应用你的RESTful API的开发者减少许多困惑。最好恪守预约的准则。 命名约定了解RESTful API的命名约定将对你井井有条地设计你的API有很大的帮忙。依据你所服务的资源来设计一个RESTful API。 举例来说,你的API用来治理作者和书籍(没错,十分经典的例子)。当初,咱们想增加一位新作者,或者通过ID3来拜访一位作者。你能够设计以下路由来实现此目标: api.com/addNewAuthorapi.com/getAuthorByID/3设想一下,一个承载了许多资源的API,每个资源都有许多属性。可能的端点列表将变得无穷无尽,而且对用户不是很敌对。所以咱们须要一种更有组织、更标准化的形式来设计API端点。 RESTful API的最佳实际形容了一个端点应该以资源名称开始,而HTTP的操作则形容了行为。当初咱们失去: POST api.com/authorsGET api.com/authors/3如果咱们想拜访ID为3的作者写过的所有书,怎么办?对于这种状况,RESTful API也有一个解决方案: GET api.com/authors/3/books最初,如果想为ID为3的作者删除ID为5的图书,该怎么办呢?同样的,让咱们遵循雷同的结构化办法来造成上面的端点: DELETE api.com/authors/3/books/5简而言之,利用HTTP操作和资源映射的结构化形式,造成一个可读的、可了解的端点门路。这种办法的最大长处是,每个开发者都理解RESTful API是如何设计的,他们能够立刻应用API,而不用浏览你的每个端点的文档。 应用复数资源资源应始终应用其复数模式。为什么?设想一下,你想检索所有的作者。因而,你会调用以下端点:GET api.com/authors 。 当你浏览申请时,你无奈判断API响应将只蕴含一个或所有作者。出于这个起因,API端点应该应用复数资源。 正确应用状态码状态码不仅仅是为了好玩,他们有明确的目标。状态码告诉客户端申请胜利。 最常见的状态码分类包含: 200 (OK):申请已胜利解决并实现。201 (Created):示意资源创立胜利。400 (Bad Request):示意客户端谬误。也就是说,申请格局不正确或短少申请参数。401 (Unauthorized):尝试拜访没有权限的资源。404 (Not Found):申请的资源不存在。500 (Internal Server Error):每当服务器在申请执行过程中引发异样时。状态码的残缺列表能够在MDN上找到。别忘了查看“I’m a teapot”状态码(418)。 遵循大小写约定最常见的是,RESTful API提供JSON数据。因而,应该履行驼峰格局的大小写约定。然而,不同的编程语言应用不同的命名约定。 如何解决搜寻、分页、过滤和排序搜寻、分页、过滤和排序等操作并不代表独自的端点。这些操作能够通过应用与API申请一起提供的查问参数来实现。 比如说,让咱们检索所有依照姓名升序排序的作者。你的API申请看起来应该长这样:api.com/authors?sort=name_asc 。 此外,我想检索名为Michiel的作者。申请看起来长这样:api.com/authors?search=Michiel 。 侥幸的是,许多API我的项目都具备内置的搜寻、分页、过滤和排序功能。这将节俭你大量的工夫。 API版本我并不常常看到这种状况,但这是对API进行版本化的最佳实际。这是向用户传播破坏性更改的无效办法。 通常,API的版本号被纳入API的URL中,比方:api.com/v1/authors/3/books。 通过HTTP头发送元数据HTTP头容许客户在其申请中发送额定的信息。例如,Authorization头部通常用于发送认证数据以拜访API。 所有可能的HTTP头的残缺列表能够在这里找到。 速率限度速率限度是一种乏味的办法,能够管制每个客户端的申请数量。上面这些是你的服务器能够返回的可能的速率限度头部: X-Rate-Limit-Limit:通知客户端在指定的工夫距离内能够发送的申请数量。X-Rate-Limit-Remaining:通知客户端在以后工夫距离内还能发送多少个申请。X-Rate-Limit-Reset:通知客户端何时重置速率限度。有意义的错误处理万一出了问题,向开发者提供一个有意义的错误信息是很重要的。比如说,Twilio的API返回以下谬误格局: { "status": 400, "message": "Resource books does not exist", "code": 24801, "more_info": "api.com/docs/errors/24801"}在这个例子中,服务器返回状态码和一个人类可读的信息。此外,还返回了一个外部错误代码,以便开发人员查找具体的谬误。这容许开发人员疾速查找无关该谬误的更多信息。 ...

September 28, 2022 · 1 min · jiezi

关于restful:什么是REST-API

前言什么是REST API?REST是Representational State Transfer(体现层状态转移)的缩写 - 对最罕用的网络服务技术简直是毫无意义的形容。REST API是两个计算机系统在web浏览器和服务器中应用HTTP技术进行通信的一种形式。在两个或多个零碎之间共享数据始终是软件开发的一个根本要求。比如说,思考购买汽车保险。你的保险公司必须取得对于你和你的车辆的信息,所以他们要求从汽车注销机构、信贷机构、银行和其余零碎取得数据。所有这些都是实时通明地产生的,以确定保险公司是否能提供一个有竞争力的保单。 API(利用程序接口)通过为零碎之间的对话提供接口来帮忙这种类型的通信。REST只是一种被宽泛驳回的API格调,咱们用它来与外部和内部以一种统一的和可预测的形式进行沟通。它能够比作咱们以前寄信时用邮票、地址和信封的形式,以确保函件被送达和浏览。 REST是人们在web零碎中罕用的交互方式。例如,在一个社交媒体利用中检索和更新账户信息。 REST API示例在你的浏览器中关上以下链接,从Open Trivia Database中申请一个随机的计算机问题: https://opentdb.com/api.php?amount=1&category=18 这是一个作为RESTful网络服务实现的公共API(它遵循REST公约)。你的浏览器将展现一个独自的JSON格局的问答问题,并附有答案。比如说: { "response_code": 0, "results": [ { "category": "Science: Computers", "type": "multiple", "difficulty": "easy", "question": "What does GHz stand for?", "correct_answer": "Gigahertz", "incorrect_answers": [ "Gigahotz", "Gigahetz", "Gigahatz" ] } ]}你能够应用任意HTTP客户端,来申请同样的URL并失去响应,比方应用curl: curl "https://opentdb.com/api.php?amount=1&category=18"HTTP客户端库能够在所有风行的语言和运行时中应用,包含JavaScript、Node.js和Deno中的Fetch以及PHP中的file_get_contents()。JSON响应是机器可读的,因而能够在输入HTML或其余格局之前被进行解析和应用。 REST APIs和Rest多年来,各种数据通信规范曾经倒退起来。你可能遇到过的抉择包含CORBA,SOAP,或者 XML-RPC。大多数都确定了严格的消息传递规定。 REST是由Roy Fielding在2000年定义的,比其余的要简略得多。它不是一个规范,而是一套对于RESTful网络服务的倡议和束缚。其中包含: 客户服务器拆散模式(Client-Server):零碎A向零碎B托管的URL收回HTTP申请,并返回一个响应。这与浏览器的工作形式雷同。浏览器对一个特定的URL发出请求,该申请被转发到一个web服务器,该服务器通常返回一个HTML页面。该页面可能蕴含对图片、样式表和JavaScript的援用,从而产生进一步的申请和响应。无状态(Stateless):REST是无状态的:客户端申请应该蕴含响应所需的所有信息。换句话说,应该能够依照任何程序收回两个或更多的HTTP申请,并且会收到雷同的响应(除非API被设计为返回随机响应)。可缓存(Cacheable):响应应该被定义为可缓存或不可缓存。缓存能够进步性能,因为没有必要为同一个URL从新生成一个响应。在某个时间段特定于某个用户的私人数据通常不会被缓存。分层(Layered):申请的客户端不须要晓得它是否在与理论的服务器、代理或任何其余中间人进行通信。创立RESTful网络服务一个RESTful网络服务申请包含: 端点URL。实现RESTful API的应用程序将定义一个或多个带有域名、端口、门路、和/或查问字符串的URL端点,例如,https://mydomain/user/123?format=json。HTTP办法。不同的HTTP办法能够在任何端点上应用,这些办法映射到应用程序的创立、读取、更新和删除(CRUD)操作:| HTTP办法 | CRUD | 行为 || --- | --- | --- || GET | 读取 | 返回申请数据 || POST | 创立 | 创立一个新记录 || PUT 或者 PATCH | 更新 | 更新已存在的记录 || DELETE | 删除 | 删除已存在的记录 |比方:- 对`/user/`的GET申请返回零碎中的注册用户列表。- 对`/user/`的POST申请应用`body`对象创立了一个ID为`123`的用户。该响应会返回ID。- 对`/user/123`的PUT申请应用`body`对象更新用户`123`。- 对`/user/123`的GET申请返回用户`123`的详情。- 对`/user/123`的DELETE申请删除用户`123`。HTTP头部。认证令牌或cookies等信息能够蕴含在HTTP申请头中。Body对象。数据通常在HTTP主体中传输,该形式与HTML<form>提交或者发送独自的JSON编码的数据字符串等形式雷同。 ...

September 21, 2022 · 2 min · jiezi

关于restful:RESTful接口的csrf防御考虑

对于RESTful规范服务是否须要办法跨站申请攻打,网上有很多探讨,总结下来外围的关键点在于是否应用了cookie,而就目前而言,REST规范下的服务接口,即使API做到了无状态,用户令牌(Token)也很常常会放到localStorage或者cookie中,这些状况下实质上与RESTful的无状态规范定义不抵触,然而必须要思考XSS(跨站脚本攻打)、CSRF(跨站申请攻打)的防备需要了。 Cookie中有个HttpOnly的属性,如果没有设置为true,应用js脚本能够拿到其中存储的内容,歹意页面可能会通过嵌入代码拿到用户未爱护的cookie值,如果存储的是用户敏感身份信息,并且网站服务没有更多的保护措施,那么黑客便能够伪造用户的身份随心所欲了。localStorage同样存在XSS平安危险,因而不利用来存储敏感数据。 CSRF在用户关上了黑客的歹意页面时产生,通过简略的嵌入<img>标签或者iframe,能在用户无感知的状况下应用用户的cookie数据拜访其余网站的GET、POST接口服务,尽管黑客得不到被爱护cookie中的值,甚至无奈解析响应报文内容,然而接口的随便伪造调用带来的危险微小。因而个别状况下网站服务须要思考csrf的平安防护。 但如果是REST规范的API服务方呢? 在REST规范下,GET办法不会产生数据批改,最多会产生一些额定的日志数据和网络流量,因而对于GET申请无需对csrf布防。而POST办法须要思考其容许的Content-Type,如果仅反对application/json格局,那么嵌入的iframe页面中的form提交产生的form-data或者form-urlencoded申请无奈胜利,而应用XmlHttpRequest发动的post申请,因为浏览器的同源策略,又会要求发动预检(preflight)申请,因而也不可能胜利。而其余Patch、Delete、Put办法的申请,既不能通过iframe达成,也无奈跳过预检申请步骤,因而在仅反对application/json的POST状况下,RESTful接口提供服务方能够不必思考csrf平安防护。 凡事无相对,在网上有一篇通过flash达成对application/json接口进行csrf攻打的办法 : 服务的校验Content-Type,只接管application/json格局的CSRF绕过办法。 不过思考到支流浏览器对flash的反对均已下线,怎么说呢...Otherwise,就不得不思考进攻下csrf攻打了。如果面临这种需要,能够从以下办法着手: 验证申请Referer,对于应用非IE6非hack版本奇葩浏览器的失常用户,拦挡下申请的Referer,只容许受信赖的起源方发动的申请,基本上就是改变最小成果最快的csrf进攻伎俩了。思考下用户token真的须要保留到cookie中吗?如果敞开浏览器就完结会话,下次从新登录能够接管,那就不要写cookie了;如果用户须要主动续签token,要做到无感知主动登录,能够写个refresh_token用来在下次申请时从新续签token(从新生成),这个token数据就存在浏览器内存变量中,而后在后续申请中携带上新的token信息,而不是写到cookie里。otherwise,用csrfToken,用户登录胜利后,或者验证身份胜利后,生成一个随机csrfToken给前端,后续申请时带上这个。csrfToken不必验证身份,只需判断是否是实在签发过的就行,能够简略放到redis汇合数据中。

July 2, 2022 · 1 min · jiezi

关于restful:RESTful接口设计译

原文链接:Web API design best practices - Azure Architecture Center | Microsoft Docs当初网络上曾经有了很多服务商的公开API,能够让各类客户端调用,那么怎么才是一个设计低劣的web API呢?一般来讲应该具备以下规范: 平台无关性:应用API的能够是任何客户端,它们不必关怀API是怎么实现的。这就要求了交互时应用到的协定要标准化,并且要存在一种机制,能确保客户端和服务提供方在数据格式上达成统一。服务演变: web API能够自行更新迭代本人的性能,应用它的客户端不必做出任何批改就能持续应用这些API。服务端提供的所有性能要具备可发现性,使得客户端能充沛应用到它们。上面来说说设计web API时要思考的一些关键问题。 什么是REST?在2000年,Roy Fielding提出应用表述性状态转移(Representational State Transfer ,简称REST)来设计网络服务的构建办法。REST是一种基于超媒体来构建分布式系统的架构格调,它不应关注底层服务如何,也不必跟HTTP绑定,不过大部分REST API的实现还是基于了HTTP协定。让咱们先关注下如何应用HTTP来设计REST API接口。 在HTTP上应用REST的益处是它是一个有公开规范的协定,不须要这些API的提供方或应用方依赖任何特定实现计划,服务方和应用方能够用任意语言、工具包来提供REST服务实现,或创立HTTP申请以及解析HTTP响应报文。 基于HTTP设计RESTful API的次要准则有: REST API的外围是资源,能够是客户能拜访到的任意物体、数据或服务每种资源都要有一个举世无双的URI来作为惟一标识符定位到该资源。比方一种客户订单能够这样形容: https://adventure-works.com/orders/1客户通过替换资源表述来与服务交互。许多web API应用JSON作为数据转换格局。例如,一个针对下面URI的GET申请可能失去如下内容: {"orderId":1,"orderValue":99.90,"productId":1,"quantity":1}REST API应用一套对立的接口,来帮忙解耦调用端和服务实现。在HTTP上构建REST API时,对立接口应用规范的HTTP动词来执行资源的操作,常常应用到的操作有GET,POST,PUT,PATCH和DELETE。REST API是无状态的。因为收回的HTTP申请是独立且无序的,因而没方法在申请间放弃这种长期会话状态。能存储信息的中央只能是API资源本身,而每个申请该当是原子操作。这样的束缚要求客户和特定服务器间不能保留任何关联,从而使得服务具备了高度的可伸缩性。任意的服务器能够解决任何的客户申请。然而,其余因素可能会限度这种可伸缩性,比方很多服务都要写数据到后端存储中,而这种繁多存储就很难扩大。对于这种数据存储该如何扩大的策略办法,能够参考Horizontal, vertical, and functional data partitioning.REST API的重要驱动外围是其表述内容中蕴含的超媒体链接。举个例子,上面展现了一个蕴含订单信息的JSON格局内容,它蕴含了一些链接,用来取得或更新与该订单关联的客户数据。 { "orderID":3, "productID":2, "quantity":4, "orderValue":16.60, "links": [ {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"GET" }, {"rel":"product","href":"https://adventure-works.com/customers/3", "action":"PUT" } ]}在2008年,Leonard Richardson提出了一个web API成熟度模型: Level 0:仅有一个URI,用来解决所有POST申请 .Level 1:不同的资源提供独自的URI申请门路.Level 2:应用HTTP的method来定义在资源上的不同操作.Level 3:应用了超媒体(HATEOS,上面会提到) 围绕资源设计APIweb API须要裸露一些业务实体, 拿电商零碎来举例,次要的实体基本上就是客户和订单了。能够发送一个蕴含了订单信息的HTTP POST申请来创立一个订单,而响应报文该当告知这个订单是否创立胜利。请记得,提供进去的这些资源操作URI应尽量应用名词(资源名)来形容,而非动词(操作动作)。 https://adventure-works.com/orders // Goodhttps://adventure-works.com/create-order // Avoid一个所谓的资源不肯定要是一个实在的物理实体,比方对于订单来说,它可能在外部实现上用到了数张关系型数据库中的表,然而对于客户而言,它就是一个独自的实体。不要创立一些仅仅是把数据库对象做了个简略镜像展现的API。REST的指标是形容实体和可在其上执行的操作,而客户不应接触到外部实现。 ...

June 22, 2022 · 4 min · jiezi

关于restful:RESTful-API-参数限制

API 查看参数常见 API创立新的学生材料的接口如下: Request POST /students HTTP/1.1Host www.school.comContent-Type application/json{ "name": "tom", "age": 12, "ethnicity": "HAN"}Response HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8{ "code": 0, "message": "success", "data": { "id": 1, "name": "tom", "age": 12, "ethnicity": "HAN" }}蕴含 name \ age \ ethnicity 三个参数,其中 ethnicity 民族,须要做查看以防止输出零碎无奈解决的内容。 例如,前端须要将民族本地化为中文,零碎不反对的民族就无奈本地化为中文。如果只有中国的 56 个民族,应用常量或者枚举都能够,实际中个别会选着应用枚举: public enum Ethnicitiy { //汉族 HAN, //蒙古族 MONGOL, // ... //基诺族 JINO;}枚举中没有设置字段存储中文含意,是为了将编码和本地化解耦。如果只有繁多语言需要,请随便Spring Web 模块默认反对枚举反序列化,谬误的数据将导致反序列化失败,这样就达到了查看参数的目标 数据耦合这样的形式能实现性能,然而会使得数据和代码耦合。例如,须要新增一个 其余 other 选项,就须要更新代码并公布新版本 很多时候还会在前端存在相似的问题。例如,后端定义了枚举之后,APP 为了本地化显示会在代码中耦合相似枚举的数据。减少新的 其余 other 选项之后,后端和 APP 都须要公布新版本,然而 APP 是否降级是由用户抉择的,如果抉择强制降级会重大影响用户体验。 ...

June 13, 2022 · 2 min · jiezi

关于restful:吐槽一下-REFTFul-API-实践中的问题

当年初生牛犊不怕虎机翻了如何设计RESTful API?,妄图在实践中用起来,在每一个禁受的我的项目中都想实现,每一次都被打脸,工夫久了就放弃医治了。接下来埋怨一些过程中遇到的坑吧。 HTTP 实现 RESTFul API 一共就3件事件,怎么就这么难呢? 看 url 就晓得操作什么看 http method 就晓得干什么看 http status code 就晓得后果难以定义 URL资源的定义就很难,教程里的案例都是很简略的状况,现实情况简单得多。例如,当初有一个需要,一个客户端 A 须要一个用户分页接口,查问条件有姓名、电话、注册工夫等。还有一个客户端 B 也须要用户分页接口,然而查问条件有姓名、罕用收货地址等。尽管都是分页查问用户,然而实现的逻辑齐全不同,个别都是离开定义 API 客户端 A 须要的接口: GET /v1/users HTTP/1.1Host: www.demo.comname=tom&phone=123321&sign_time=2011-01-01客户端 B 须要的接口: GET /v1/users/with-common-address HTTP/1.1Host: www.demo.comname=tom&phone=123321&common_address=abc%20efg越是根底数据,越容易遇到多端须要的参数不同,要求返回的数据也不同。特地是遇到实现办法天壤之别的时候,通常都是通过加后缀或者前缀的形式辨别。 然而这个例子是能够用一个接口实现的,程序上是能够通过是否蕴含 commons_address 参数辨别不同的解决逻辑。然而这里有个很事实的问题,如果第一个接口曾经存在并且工作良好的话,作为承受第二个接口开发的你,有勇气去批改第一个接口的实现吗?特地是在逻辑很简单,第一个接口的代码可读性不高的时候。 除了以上的问题,当相似的需要过多时,这个接口的参数将越来越多,返回的音讯构造蕴含的字段也越来越多。参数该怎么传,什么状况下哪些参数不能传。返回音讯体每个字段什么时候会有值,什么时候没有值。这些都须要在逻辑里实现,并且测试,最初还得同步到 API 文档中。这样的代码难以保护,接口文档难以浏览。 临时没有找到比加后缀老本更低的形式,或者能够思考加一个中间层,毕竟 计算机领域的任何问题都能够通过减少一个间接的中间层来解决 被弃用的状态码应用服务器通常只返回 200、401、500 错误码,其中 401 通常还是由权限框架实现的。更常见的是自定义 code 示意谬误起因,如果没有国际化需要的话,大概率这个 code 只有两个值,一个示意胜利,一个示意业务逻辑谬误。就像上面的例子: HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8{ "code": 123321, "message": "手机号曾经被占用"}实质的起因还是大部分业务逻辑上的谬误,不须要非凡解决只须要提醒就行。所以只须要辨别: 胜利 http 200, code 0业务逻辑谬误 http 200, code 不等于 0代码谬误 http 500而且不同 HTTP 状态码,须要编码实现如何解决,然而实现的成果都是把应用服务器返回的音讯提醒一下而已。这里的坑在于提示信息是硬编码在应用服务器的代码中,如果想改一下提示信息就得公布代码。 ...

May 30, 2022 · 1 min · jiezi

关于restful:RESTful和SOAP的区别

一、Web Service Web Service服务通常被定义为一组模块化的API,它们能够通过网络进行调用,来执行近程零碎的申请服务。Web service是一个平台独立的,低耦合的,自蕴含的、基于可编程的web的应用程序,可应用凋谢的XML规范来形容、公布、发现、协调和配置这些应用程序,用于开发分布式的互操作的应用程序。各应用程序通过网络协议和规定的一些规范数据格式(Http,XML,Soap)来拜访Web Service,通过Web Service外部执行失去所需后果。根据Web Service标准施行的利用之间, 无论它们所应用的语言、 平台或外部协定是什么, 都能够相互交换数据。Web Service为整个企业甚至多个组织之间的业务流程的集成提供了一个通用机制。 实际上,Web Service的次要指标是跨平台的可互操作性。为了达到这一指标,Web Service齐全基于XML(可扩大标记语言)、XSD(XMLSchema)等独立于平台、独立于软件供应商的规范,是创立可互操作的、分布式应用程序的新平台。Web service平台必须提供一套规范的类型零碎,用于沟通不同平台、编程语言和组件模型中的不同类型零碎。 根本元素 Web Service的三种根本元素:SOAP、WSDL和UDDI。 SOAP 是一种基于XML协定、用于扩散或散布的环境中替换信息的简略协定。 WSDL ( 网络服务描述语言,Web Services Description Language)是一门基于 XML 的语言,用于形容 Web Services 以及如何对它们进行拜访。web service 描述语言 (WSDL) 就是这样一个基于 XML 的语言,用于形容 web service 及其函数、参数和返回值。因为是基于 XML 的,既可供人浏览,也可应用工具生成调用相应 web service 的代码。实例如下: <?xml version="1.0" encoding="UTF-8" ?><definitions targetNamespace="http://schemas.xmlsoap.org/HelloService" xmlns:tns="http://schemas.xmlsoap.org/HelloService" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <!-- (参数类型) 数据类型定义,个别应用XML Schema中的类型零碎 --><types> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schemas.xmlsoap.org/HelloService" attributeFormDefault="qualified" elementFormDefault="qualified" > <xsd:element name="sayHello"> <xsd:complexType> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="1" name="name" nillable="true" type="xsd:string" /> </xsd:sequence></xsd:complexType></xsd:element> <xsd:element name="sayHelloResponse"> <xsd:complexType> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="1" name="name" nillable="true" type="xsd:string" /> </xsd:sequence></xsd:complexType></xsd:element> </xsd:schema></types><!-- (操作中的参数) 应用Types所定义的类型来定义整个音讯的数据结构 --><message name="sayHelloRequest"> <part name="parameters" element="tns:sayHello" /></message><message name="sayHelloResponse"> <part name="parameters" element="tns:sayHelloResponse" /></message><!-- (提供的操作) portType形容了一组操作 --><portType name="HelloServicePortType"> <operation name="sayHello"> <input name="sayHelloRequest" message="tns:sayHelloRequest" /> <output name="sayHelloResponse" message="tns:sayHelloResponse" /> </operation></portType><!-- (绑定通信协议)形容如何将portType映射成传输/消息传递协定,binding将portType绑定到特定的协定(例如 SOAP、HTTP或MIME)--><binding name="HelloServicePortBinding" type="tns:HelloServicePortType"> <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" /> <operation name="sayHello"> <!-- 元素的soapAction属性被转换成HTTP头 --> <soap:operation soapAction="" /> <input name="sayHelloRequest"> <soap:body use="literal" /> </input> <output name="sayHelloResponse"> <soap:body use="literal" /> </output> </operation></binding><!-- (有特定操作的服务)service列出某个特定绑定的连贯信息。服务能够有一个或多个端口,每个端口都定义一个不同的连贯办法(例如HTTP/SMTP等等) --><service name="HelloService"> <port name="HelloServiceHttpPort" binding="tns:HelloServicePortBinding"> <soap:address location="http://localhost:8080/services/HelloService" /> </port></service></definitions>View Code UDDI ( 通用形容、发现与集成服务,Universal Description, Discovery and Integration) UDDI 是一种目录服务,企业能够应用它对 Web services 进行注册和搜寻。UDDI 是一种由 WSDL 形容的 ,用于存储无关 web services 的信息的目录,应用 SOAP、XML、HTTP 和 DNS 协定。UDDI是一种标准,它次要提供基于Web服务的注册和发现机制,为Web服务提供三个重要的技术支持:①规范、通明、专门形容Web服务的机制;②调用Web服务的机制;③能够拜访的Web服务注册核心。 ...

April 23, 2022 · 2 min · jiezi

关于restful:RestfulSOAPRPCSOA微服务之间的区别

什么是RestfulRestful是一种架构设计格调,提供了设计准则和约束条件,而不是架构,而满足这些约束条件和准则的应用程序或设计就是 Restful架构或服务。次要的设计准则: 资源与URI 对立资源接口(HTTP办法如GET,PUT和POST) 资源的表述 资源的链接 状态的转移总之,RESTful的外围就是后端将资源公布为URI,前端通过URI拜访资源,并通过HTTP动词示意要对资源进行的操作。什么是SOAP简略对象拜访协定是一种数据交换协定标准,是一种轻量的、简略的、基于XML的协定的标准。SOAP协定和HTTP协定一样,都是底层的通信协议,只是申请包的格局不同而已,SOAP包是XML格局的。SOAP的音讯是基于xml并封装成了合乎http协定,因而,它合乎任何路由器、 防火墙或代理服务器的要求。SOAP能够应用任何语言来实现,只有发送正确的soap申请即可,基于soap的服务能够在任何平台无需批改即可失常应用。RPCRPC 的全称是 Remote Procedure Call 是一种过程间通信形式。它容许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不必程序员显式编码这个近程调用的细节。即无论是调用本地接口/服务的还是近程的接口/服务,实质上编写的调用代码基本相同。比方两台服务器A,B,一个利用部署在A服务器上,想要调用B服务器上利用提供的函数或者办法,因为不在一个内存空间,不能间接调用,这时候须要通过就能够利用RPC框架的实现来解决 RPC就是从一台机器(客户端)上通过参数传递的形式调用另一台机器(服务器)上的一个函数或办法(能够统称为服务)并失去返回的后果。RPC 会暗藏底层的通信细节(不须要间接解决Socket通信或Http通信)RPC 是一个申请响应模型。客户端发动申请,服务器返回响应(相似于Http的工作形式)RPC 在应用模式上像调用本地函数(或办法)一样去调用近程的函数(或办法)。4种典型RPC近程调用框架(1)RMI实现,利用java.rmi包实现,基于Java近程办法协定(Java Remote Method Protocol)和java的原生序列化。(2)Hessian,是一个轻量级的remoting onhttp工具,应用简略的办法提供了RMI的性能。 基于HTTP协定,采纳二进制编解码。(3)thrift是一种可伸缩的跨语言服务的软件框架。thrift容许你定义一个形容文件,形容数据类型和服务接口。根据该文件,编译器不便地生成RPC客户端和服务器通信代码。(4)Dubbo是阿里巴巴公司开源的一个高性能优良的服务框架,使得利用可通过高性能的 RPC 实现服务的输入和输出性能,能够和 Spring框架无缝集成(5)还有SpringCloud框架,微服务全家桶。为开发人员提供了疾速构建分布式系统的一些工具,包含配置管理、服务发现、断路器、路由、微代理、事件总线、全局锁、决策竞选、分布式会话等等。微服务在实质上,就是rpc。rpc有基于tcp的,http的,mq的等等。spring cloud是基于spring boot的,spring boot 实现的是http协定的rpc,算是rpc的一个子集。什么是SOASOA(Service-Oriented Architecture),中文全称:面向服务的架构。艰深点来讲,SOA提倡将不同应用程序的业务性能封装成“服务”并宿主起来,通常以接口和契约的模式裸露并提供给外界利用拜访(通过替换音讯),达到不同零碎可重用的目标。SOA是一个组件模型,它能将不同的服务通过定义良好的接口和契约分割起来。服务是SOA的基石。 业务零碎合成为多个组件,让每个组件都独立提供离散,自治,可复用的服务能力通过服务的组合和编排来实现下层的业务流程作用:简化保护,升高整体危险,伸缩灵便 微服务架构什么是微服务架构架构设计概念,各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它个性(繁多职责,边界,异步通信,独立部署)是分布式概念作用:各服务可独立利用,组合服务也可零碎利用(巨石利用[monolith]的简化实现策略-平台思维) 微服务和SOA的区别微服务是SOA架构演进的后果。两者说到底都是对外提供接口的一种架构设计形式,随着互联网的倒退,简单的平台、业务的呈现,导致SOA架构向更细粒度、更通过化水平倒退,就成了所谓的微服务了。总之,微服务是SOA倒退进去的产物,它是一种比拟现代化的细粒度的SOA实现形式。SOA与微服务的区别在于如下几个方面: 微服务相比于SOA更加精密,微服务更多的以独立的过程的形式存在,相互之间并无影响; 微服务提供的接口方式更加通用化,例如HTTP RESTful形式,各种终端都能够调用,无关语言、平台限度; 微服务更偏向于分布式去中心化的部署形式,在互联网业务场景下更适宜。 SOA架构次要针对企业级、采纳ESB服务(ESB企业服务总线),十分重,须要序列化和反序列化,采纳XML格局传输。 微服务架构次要互联网公司,轻量级、玲珑,独立运行,基于Http+Rest+JSON格局传输。为什么要应用微服务?技术为业务而生,架构也为业务而呈现,当然SOA和微服务也是因为业务的倒退而呈现。https://www.cnblogs.com/wwct/...平台随着业务的倒退从 All in One 环境就能够满足业务需要(以Java来说,可能只是一两个war包就解决了)。倒退到须要拆分多个利用,并且采纳MVC的形式拆散前后端,放慢开发效率;在倒退到服务越来越多,不得不将一些外围或共用的服务拆分进去,其实倒退到此阶段,如果服务拆分的足够精密,并且独立运行,我感觉就能够将之了解为一个微服务了。

April 23, 2022 · 1 min · jiezi

关于restful:搭建-Restful-Web-服务

1. 了解 REST REST 全称是 Representational State Transfer,中文意思是表征性状态转移。它首次呈现在2000年Roy Fielding的博士论文中,Roy Fielding是HTTP标准的次要编写者之一。值得注意的是REST并没有一个明确的规范,而更像是一种设计的格调。如果一个架构合乎REST的约束条件和准则,咱们就称它为RESTful架构。 实践上REST架构格调并不是绑定在HTTP上,只不过目前HTTP是惟一与REST相干的实例。 1.1. REST 准则资源 可通过目录构造款式的 URIs 裸露表述 能够通过 JSON 或 XML 表白的数据对象或属性来传递音讯 应用对立的 HTTP 办法(例如:GET、POST、PUT 和 DELETE)无状态 客户端与服务端之间的交互在申请之间是无状态的,从客户端到服务端的每个申请都必须蕴含了解申请所必须的信息 1.2. HTTP 办法 应用 HTTP 将 CRUD(create, retrieve, update, delete <创立、获取、更新、删除—增删改查>)操作映射为 HTTP 申请。如果依照HTTP办法的语义来裸露资源,那么接口将会领有安全性和幂等性的个性,例如GET和HEAD申请都是平安的, 无论申请多少次,都不会扭转服务器状态。而GET、HEAD、PUT和DELETE申请都是幂等的,无论对资源操作多少次, 后果总是一样的,前面的申请并不会产生比第一次更多的影响。 1.2.1. GET平安且幂等获取信息 1.2.2. POST不平安且不幂等应用申请中提供的实体执行操作,可用于创立资源或更新资源 1.2.3. PUT不平安但幂等应用申请中提供的实体执行操作,可用于创立资源或更新资源 1.2.4. DELETE不平安但幂等删除资源 POST和PUT在创立资源的区别在于,所创立的资源的名称(URI)是否由客户端决定。 例如为我的博文减少一个java的分类,生成的门路就是分类名/categories/java,那么就能够采纳PUT办法。不过很多人间接把POST、GET、PUT、DELETE间接对应上CRUD,例如在一个典型的rails实现的RESTful利用中就是这么做的。 1.3. HTTP status codes 状态码批示 HTTP 申请的后果: 1XX:信息2XX:胜利3XX:转发4XX:客户端谬误5XX:服务端谬误 1.4. 媒体类型 HTTP头中的 Accept 和 Content-Type 可用于形容HTTP申请中发送或申请的内容。如果客户端申请JSON响应,那么能够将 Accept 设为 application/json。相应地,如果发送的内容是XML,那么能够设置 Content-Type 为 application/xml 。 ...

March 9, 2022 · 4 min · jiezi

关于restful:restful-url设计规范参考

1、url命名格调介绍 驼峰命名法 例:http://xxxx/getUser蛇形命名法 例:http://xxxx/get_user脊柱命名法 示例https://help.github.com/articles/why-are-my-commits-linked-to-the-wrong-user/#commits-are-not-linked-to-any-user https://stackoverflow.com/questions/5262224/how-are-reddit-and-hacker-news-ranking-algorithms-used https://api.github.com/2、url大小写及多单词命名准则 1) URL采纳小写字母,数字,局部特殊符号(非制表符)组成。 2) URL中不采纳大小写混合的驼峰命名形式,尽量采纳全小写单词,如果须要连贯多个单词,则采纳连接符“_”或"-"连贯单词,举荐应用后者,即中横线"-"。 3、url门路分级准则 第1级:/api 用于辨别服务接口和动态资源申请,以便做权限拦挡等解决。第2级:/模块名称 对应controller名称,模块名称能够定义两级,即:/父模块名/子模块名,通常将第1级和第2级一起定义到controller门路中,例:/api/user第3级:/动作名称/版本号 对应method名称,例:/add/v14、crud url设计举荐 新增:/api/{module-name}/add/v1 申请形式:post,content-type:application/json 查看:/api/{module-name}/get/{id}/v1 应用场景:进入编辑页面,以及相干详情弹框 申请形式:get,参数放在url中 批改:/api/{module-name}/edit/{id}/v1 申请形式:post,content-type:application/json 删除:/api/{module-name}/del/{id}/v1 申请形式:post,content-type:application/json 列表(默认,带分页,带搜寻):/api/{module-name}/list/v1 申请形式:get,参数放在url中 倡议:不管是否有分页需要,后果全副按分页格局返回,默认可不传分页参数 列表(非默认):/api/{module-name}/list-{datatype}/v1

March 2, 2022 · 1 min · jiezi

关于restful:一个没有-Postman-好用的工具不试一下

忘了 postman 是被谁种草的,很长一段时间内 postman 都是我做接口测试的首选工具,之前也有小伙伴跟我安利过 IDEA 中的 RestfulToolkit 插件,然而始终没机会体验,最近抽空玩了一把,感觉在某些场景下还蛮不错的(不须要认证的场景下),和小伙伴们分享下。 1. RestfulToolkitRestfulToolkit 是一套 RESTful 服务开发辅助工具集,它次要提供了如下性能: 依据 URL 间接跳转到对应的办法定义 ( Ctrl \ or Ctrl Alt N );提供了一个 Services tree 的显示窗口;一个简略的 http 申请工具;在申请办法上增加了有用性能: 复制生成 URL;,复制办法参数...其余性能: java 类上增加 Convert to JSON 性能,格式化 json 数据 ( Windows: Ctrl + Enter; Mac: Command + Enter )。它反对 Spring 体系 (Spring MVC / Spring Boot 1.x,2.x);反对 JAX-RS;反对 Java 和 Kotlin 语言。 2. 装置在 IDEA 中抉择 File->Plugins,而后搜寻 RestfulToolkit,如下: ...

March 2, 2022 · 1 min · jiezi

关于restful:页面录制服务上线RESTful-API-调用实现所见所录即所得

咱们为很多实时互动场景提供了服务。在一些场景中,用户不仅须要实时互动,还须要把互动的过程录下来。那么一个好的录制解决方案到底须要具备哪些特色呢? 在答复这个问题之前,先聊一下客户应用录制的起因。一般来讲,用户应用录制性能的起因次要有三种: 1. 质检。 比方在教育场景下,须要通过回放录制来查看课程品质,在社交直播或金融双录场景下,须要保留录制视频,做合规性审查。 2. 留证。 如教育、医疗、音视频客服等场景,需存档留证以应答可能的纠纷。这种场景下,对录制计划的外围诉求是内容完整性,不能容忍哪怕是秒级的视频丢录。 3. 回放。 比方在教育场景、直播场景下,用户心愿观看回放。这也是大多数实时互动场景里应用录制的次要起因。 那么在这种场景下,怎么才算是一个好的录制解决方案呢? 能够从五个维度来掂量录制计划: 录制成果:须要还原实在的互动场景,包含音视频、课件、白板、聊天信息等所有元素。同时,不能对主播音视频互动体验造成任何负面影响。集成难度:越简略越好,最好是不须要开发。期待时长:期待时长越短越好,最好是录制完结后能够立刻回放。文件兼容性:任何平台、任意浏览器都能够播放。文件迁徙的便利性:文件下载、上传等迁徙过程要非常简单,便于录制文件治理。为了解决各种场景的录制需要,目前有两种比拟支流的计划。 一、音视频、白板等元素别离录制,而后拼接回放次要思路是将音视频、白板、课件、PPT、聊天内容等别离录制下来,录制完结后再别离回放,并通过工夫戳对齐播放进度。这种计划的益处是,白板、课件、聊天内容等均以数据模式回放,能够保留原有的实在互动成果,例如 PPT 能够独自翻页,灵活性较好。但其毛病也非常明显: 1. 集成难度大。 须要同时开发音视频录制、白板录制、聊天内容的录制,特地是各不同元素须要通过工夫戳对齐回放,要做到十分好的同步成果需投入较多开发精力。 2. 播放兼容性受限。 这种形式只能通过非凡播放器来回放,无奈很好地兼容支流播放器。 3. 等待时间长。 为了解决播放兼容性问题,往往须要在录制完结后进行离线解决,转成一个残缺的 MP4 文件,这个过程等待时间较长,还会带来额定的转码老本。 二、本地客户端录屏 不论是本地客户端录制,还是通过屏幕共享将屏幕流发送到云端进行录制,其本质都是在用户的本地客户端上捕捉屏幕内容。这种计划的益处是所见即所得,回放成果跟实在互动场景能够保持一致。但其毛病也是相当显著: 1. 影响本地用户的 RTC 互动体验。 本地捕捉屏幕内容会极大地耗费终端设备的计算资源,如果要实时上传,还会占用主播上行的带宽资源,这些都会影响本地用户的音视频通话体验甚至会呈现卡顿、含糊等重大的结果,这对一个实时互动场景来说是难以承受的致命缺点。 2. 集成难度大。 开发者须要在端上进行开发,须要解决文件本地存储、上传等问题,往往还须要解决简单的混音问题,集成门槛十分高。 除了以上两种支流思路,是否还有其余更好的计划呢? 声网Agora提出了第三种新思路:页面录制页面录制是指通过 Web 页面渲染的形式, 在服务端同步录制音视频、白板、课件以及聊天信息等,还原实在的互动场景。其原理是:开发者通过 RESTful API 发动录制申请,并将待录制页面的 URL 以申请参数的模式发给 Agora 录制服务,Agora 录制服务会关上该Web页面,并以录屏的形式实时录制生成 MP4 文件,上传至指定的第三方云存储平台。具体 页面录制 文档,可点击「浏览原文」浏览。 依据此前录制计划判断维度,将页面录制与此前咱们列举的录制计划相比: 在集成上,通过Restful API发动申请录制,简略易用。录制成果实现所见即所得的,将音视频、白板、课件以及聊天信息等内容全副同时录制下来,且不带来额定的带宽、性能开销,录制过程不影响任何主播/观众的RTC互动体验。录制完结后,能够实时生成MP4文件,兼容各支流播放器。文件下载非常简单,便于录制文件治理。同时,页面录制具备录制任意网页页面的能力,所以用 WebRTC 或其它计划自研 RTC 性能的开发者同样能够应用。

October 26, 2021 · 1 min · jiezi

关于restful:REST-API知识整理

一、介绍(REST) REST即Representational State Transfer的缩写,"体现层状态转化"。REST API是前后端拆散的最佳实际,是开发的一套规范或者说是一套标准,不是框架。益处:1、轻量,间接通过http,不须要额定的协定,通常有post/get/put/deletec等操作;2、面向资源,高深莫测,具备自解释性;3、数据形容简略,个别通过json或者xml实现数据间的通信。 二、资源(Resources) REST的名称"体现层状态转化"中,省略了主语。"体现层"其实指的是"资源"(Resources)的"体现层"。所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它能够是一段文本、一张图片、一种服务,总之就是一个具体的切实。你能够用一个URI(对立资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,拜访它的URI就能够,因而URI就成了每一个资源的地址或举世无双的辨认符。所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。三、体现层(Representation) "资源"是一种信息实体,它能够有多种外在表现形式。"资源"具体出现进去的模式,叫做它的"体现层"(Representation)。比方,文本能够用txt格局体现,也能够用HTML格局、XML格局、JSON格局体现,甚至能够采纳二进制格局;图片能够用JPG格局体现,也能够用PNG格局体现。URI只代表资源的实体,不代表它的模式。严格地说,有些网址最初的".html"后缀名是不必要的,因为这个后缀名示意格局,属于"体现层"领域,而URI应该只代表"资源"的地位。它的具体表现形式,应该在HTTP申请的头信息中用Accept和Content-Type字段指定,这两个字段才是对"体现层"的形容。四、状态转化(State Transfer) 拜访一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必波及到数据和状态的变动。互联网通信协议HTTP协定,是一个无状态协定。这意味着,所有的状态都保留在服务器端。因而,如果客户端想要操作服务器,必须通过某种伎俩,让服务器端产生"状态转化"(State Transfer)。而这种转化是建设在体现层之上的,所以就是"体现层状态转化"。客户端用到的伎俩,只能是HTTP协定。具体来说,就是HTTP协定外面,四个示意操作形式的动词:GET、POST、PUT、DELETE。别离对应四种基本操作:GET用来获取资源,POST用来新建资源(也能够用于更新资源),PUT用来更新资源,DELETE用来删除资源。五、综述综合下面的解释,总结一下什么是RESTful架构:1、每一个URI代表一种资源;2、客户端和服务器之间,存在传递这种资源的某种体现层;3、客户端通过四个HTTP动词,对服务器端资源进行操作,实现"体现层状态转化"。

October 14, 2021 · 1 min · jiezi

关于restful:布道APIREST-从来都不是基于-CRUD

一个重大的误会是 REST 的 API 必须是基于 CRUD 的,这两者之间没有任何的分割,都只是API设计格调的一种形式而已。本文还将介绍基于 REST 的 API 的几种实现规定。 CRUD简介基于 CRUD 的 API 是指提供蕴含实例的资源汇合的 API,效仿 create、read、update 和 delete 生命周期模式。当有一组代表内容或状态的资源实例时,CRUD 模式很有用,它通常遵循以下模式: GET /articles – 列出/分页/过滤文章列表POST /articles – 创立新文章GET /articles/{articleId} – 获取文章详情PATCH /articles/{articleId} – 更新文章详情DELETE /articles/{articleId} – 删除指定ID的文章REST:超过 CRUD在探讨 CRUD 之外的API格调之前,须要廓清一个对于 REST 的重大误会: CRUD 并不是对“RESTful”API 的要求。事实上,在 Fielding’s dissertation 的论文中并没有提及 CRUD 。 在谈到基于 REST 的 API 时,很多人将利用基于 CRUD 格调的资源与 REST 格调混同一起。然而,这些其实是 HTTP 形象的资源概念,与数据库设计没有间接关系: 本标准没有限度可能是资源,相同“资源”一词是在个别意义上应用的用于任何可能被 URI 标识的内容。—— RFC 3986REST 是标准客户端和服务端之间通信的形式,利用 HTTP 协定提供的性能,这些限度能够自在地专一于 API 设计: ...

June 20, 2021 · 2 min · jiezi

关于restful:布道-API浅谈-API-设计风格

API 格调是一个备受争议的话题,大多数开发者都相熟 REST 与 GraphQL 的争执,更不用说其余格调了。本文将介绍常见的8种不同的API格调。 API有哪些格调呢?依照不同的特色能够分为五类,如下: Web API:REST 和 so-called REST查问 API:GraphQL公布订阅 API:包含 Kafka 和 WebSubRPC API :SOAP 和 gRPC一般的文件传输束缚、属性具体应用哪种API格调,个别须要思考束缚、属性和关键因素,这里不思考架构的影响因素。 思考以下束缚和属性之间关系的示例: 解耦(束缚)的 API 实质上是可批改的(属性)无状态(束缚)的 API 实质上是牢靠的(属性)和可扩大的(属性)实现对立接口(束缚)的 API 实质上是简略的(属性)当然,束缚也会导致负面属性,例如,效率低下是一种可能由对立接口的束缚引起的。然而,要抉择正确的 API 格调,须要思考对我的项目团队因素(效率、品质、老本)。 要抉择想要的属性,须要思考团队的局限性。 业务限度:例如用例、客户要求、公布工夫地区限度:例如地区政策限度技能限度:例如团队文化、团队技能复杂性限度:包含格调难度、可扩展性需要和算法复杂性除了下面的思考因素,你可能发现要构建具备特定属性的API,还有一些常见的思考因素,如可移植性、可见性和安全性。是的,上面的因素也是须要思考的: 性能可扩展性易用性可修改性可靠性可发现性易于开发老本和后面的因素相比,上面的因素不那么要害但又不得不思考: 成熟度企业适用性工具社区特定格调的资源易于凋谢抉择正确 API 格调的倡议:综合思考以上的因素就会晓得抉择哪种格调,但要做到这一点,还须要晓得常见的八种 API 格调在束缚、特色。RESTREST(REpresentational State Transfer),首次呈现在 2000 年 Roy Thomas Fielding 的博士论文中,提出了一种万维网的软件架构格调-REST(全称:Representational State Transfer, 体现层状态转换),目标是便于在不同软件/程序中(例如互联网)中相互传递信息。它指的是一组架构约束条件和准则。满足这些约束条件和准则的应用程序或设计就是 REST 的。 束缚:客户端-服务器、无状态、可缓存、分层零碎、按需代码、对立接口 特色:性能、简略性、可扩展性、可见性、可移植性、可发现性、可修改性、可靠性 REST 非常适合构建长久的可扩大零碎。它也是一种绝对灵便和成熟的格调,因而实用于内容协商、多媒体和顶级身份验证等内容。So-called RESTcalled和so-called都示意所谓,区别在于,前者是名词性从句,后者是一个形容词,因而在REST的设计上次要在于端点的设计。 束缚:客户端-服务器、无状态、可缓存、分层零碎、按需代码、对立接口 特色:性能、简略性、可扩展性、可见性、可移植性、可发现性、可靠性 So-called REST 是大多数团队的首选,因为遵循 HTTP 协定并提供 REST 的所有属性,对立接口。可怜的是,没有这种束缚意味着 So-called REST 提供的可修改性很差。GraphQLGraphQL是一个开源的,面向API而发明进去的数据查问操作语言以及相应的运行环境。 于2012年仍处于Facebook外部开发阶段,直到2015年才公开公布。 2018年11月7日,Facebook将GraphQL我的项目转移到新成立的GraphQL基金会。 ...

June 20, 2021 · 1 min · jiezi

关于restful:开源接口管理工具基于Vue和Eelctron

传送门GitHub地址Gitee地址残缺文档 前言【高兴摸鱼】是一款基于Vue和Electron的开源接口管理工具。最后构建这个我的项目的时候是为了学习Node.js和解决团队前后端协调问题。社区中有 YApi 、Rap2、Doclever、 Nei、Swagger、Apidoc等开源解决方案,同时也存在 Postman、Eolinker、ApiPost等商业解决方案。 在这之前团队尝试了YApi和Rap2等社区计划,他们可能满足一些根本的需要,然而在深刻应用当前,还是呈现了一些影响效率的问题。过后应用这两个工具最大的问题就是接口无奈反对多级嵌套,某些我的项目接口多了当前会导致检索效率大大降低。于是尝试从头开始写一款接口管理工具。 团队合作 十分外围的一个性能,在前后端拆散状况下,一套简洁的团队管理策略会大大提高分工效率。咱们将权限分为 只读、读写、管理员三类。 只读:只容许用户查看文档(个别能够给前端开发人员赋予这个角色)读写:容许对文档进行增删改查等(个别能够给后端开发人员赋予这个角色)管理员:除了读写以外还能够减少或者删除用户 下面的三种角色能够满足大部分日常应用需要。在一些非凡状况下你可能须要更加细粒度的权限管制,比方:领有读写权限的用户你只心愿他能编辑文档,但不心愿他能导出全副文档。咱们提供了自定义角色性能,能够准确到每一个接口和路由(个别状况下用不到)。 【权限治理】 目录导航 十分外围的一个性能,设计一个不便并且易使用的目录导航可能大大加强录入体验。咱们从其余开源我的项目issue中总结了一些常见需要。 反对有限级别嵌套反对拖拽文件反对拖拽目录反对单个或者批量拷贝文件反对单个或者多个拷贝目录反对一些右键菜单可能批量删除导航栏接口要与目录联动开展 工具实现了下面的全副性能,同时也扩大了一些实用的性能。 单个目录上面领有超过10个文档会升高文档检索效率,默认状况下工具限度单个目录只容许8个文档存在,当然也容许你自定义限度个数。因为目录导航空间无限,工具容许你应用Ctrl + 鼠标左键来查看节点的额定信息【批量删除节点】 【批量文档复制粘贴】 标签导航 标签导航是为了不便开发人员在多个接口之间疾速切换,开源的产品这块做的并不是很欠缺,咱们在实践中总结了这些需要。 顶部导航标签要与左侧目录导航一起联动导航标签要容许拖拽导航标签须要鼠标右键操作在文档产生变动的时候导航标签须要有小圆点提醒导航标签须要标注申请类型 大部分商用的接口工具都具备导航标签性能,然而开源产品这块大都不具备标签导航性能或者性能完成度不高【导航栏相干操作】 接口调试(模仿申请) 大部分的接口工具都会内置接口调试性能,这样开发人员只须要应用一个工具就能实现接口调试和接口录入。不过因为浏览器自身的限度(同源策略),间接在web端发动HTTP申请大概率会失败。这里列举了一些常见的解决方案。 计划毛病浏览器插件插件装置、Cookie反对度不高远端服务器代理转发相似192.168.0.0这种内网网段无法访问应用客户端(Electron)须要装置客户端从技术上来说,应用客户端来躲避同源策略是一种比拟好的实际,同时依靠客户端弱小的api还能实现很多web端无奈实现的事件,当然装置客户端也会给用户带来一些不不便。目前支流的商业我的项目大都采纳客户端的模式来为用户提供接口调试性能,局部工具甚至不提供web端的应用。咱们采纳了客户端的形式来实现接口调试,同时也保留了web端的应用性能,除了接口调试和Mock性能无奈应用外,web端和客户端在性能上没有其余区别。咱们也会在将来提供浏览器插件性能,这样用户就能够在web端应用接口调试性能了 咱们总结一些常见的接口调试需要 反对常见的GET、POST、PUT、DELETE、OPTIONS、PATCH反对RESTFUL类型查问参数反对 x-www-form-urlencoded、multipart/form-data、json、xml、binary等常见MIME类型multipart/form-data反对传递文件反对自定义Header反对发送Cookie反对自定义变量反对格式化的预览返回值,json,text/html,image等类型前置操作,后置操作websocket 【非JSON类型格局数据返回展现】 【json类型返回数据展现】 接口录入 对接口的增删改查是整个接口工具最外围的局部,常见的开源产品对 申请参数(Body),多个返回参数方面反对比拟弱。咱们总结了在典型业务场景下,接口录入应该蕴含以下外围模块。 申请办法和申请URL录入接口标签录入申请参数(Params) /api/demo?id=3&name=lee申请参数(Path) /api/demo/{id}申请参数(Body) noneform-data(能够传文件)x-www-form-urlencoded(表单提交形式)application/jsontext/plain、text/plain、application/javascript、application/xml等 MIME类型binary申请头返回参数(通常来说能够增加多个返回参数)备注信息(富文本格式)草稿状态变量反对反对丰盛的参数类型 Number、Integer、String、Array、Object、File、Binary、Boolean、Null、Enum 接口工具辨别body录入形式,目标是对某些特定类型数据进行格式化解决,这些数据都通过HTTP body传输,程序通过header外面的Content-Type辨别数据格式。【接口录入工作区】 除了欠缺必要的接口模块,工具还在录入效率方面想了很多方法。咱们从Yapi、Rap2等开源我的项目issue中整顿用户常见的录入需要。 每个字段的录入都心愿存在补全性能参数模板性能json类型数据导入性能,并且能主动补全备注发送模仿申请时,心愿能将实在返回值利用于文档【字段快捷补全】 【参数模板】 【json数据导入】 【返回参数利用于文档】 导入性能 目前市面上接口工具总类繁多,在解决导入的时候会有以下几个次要问题。 大部分接口工具并没有严格的数据格式定义以后零碎数据接口并不能很好的和第三方接口兼容局部工具不提供可解决格局数据(JSON,YAML等)导出性能openapi/swagger这类标准细节十分丰盛,须要解决大量逻辑openapi/swagger这类标准,用户未能严格依照标准书写导致导入出错导入数据会有一些额定的配置需要(eg: 指定导出地位,指定导出数据量等) 目前比较稳定和被广泛认可的标准是OpenAPI标准,很多商业化的工具都是反对这种标准的。postman这类工具领有十分大的市场占有率,大部分工具也都反对这种类型数据导入。咱们收集了一些罕用的接口工具,并且列出了工具对导入的反对状况。 文档类型反对度备注Openapi/Swagger反对单个文件、反对3.0及以上版本、兼容常见语法规定、提供长期反对openpai标准领有十分丰盛的实现细节,工具将会长期欠缺并且兼容这个标准、将来会提供多文件导入反对postman反对2.1及以上版本、兼容常见语法规定、提供长期反对Yapi反对导入、兼容常见语法规定开源社区十分闻名的接口工具,遗憾的是曾经不再保护,咱们提供迁徙反对Rap2暂不反对筹备反对ShowDoc暂不反对 Nei暂不反对 Docway暂不反对 ApiPost暂不反对 Eolinker暂不反对筹备反对在惯例导入需要下面,咱们扩大了一些性能,进步了局部我的项目内迁徙效率。 容许多个我的项目之间相互导出数据容许用户指定导入时候挂载目录【我的项目内相互导入】 【额定导入数据配置】 导出性能 导出性能一方面是不便用户分享文档给第三方用户,另一方面也提供了肯定的迁徙和备份能力。上面是一些常见的导出场景 导出格局是否反对备注JSON(摸鱼格局)反对高兴摸鱼.json、提供标准接口数据导出,不便用户备份或者迁徙到其余接口工具、反对指定内容导出HTML反对高兴摸鱼.html、提供十分残缺的预览性能反对、反对指定内容导出Markdown暂不反对筹备反对PDF暂不反对筹备反对Word反对无限基于officegen、目录生成性能缺失、款式较为俊俏OpenApi暂不反对筹备反对能够应用openapi丰盛的工具链 生成在线链接反对在线链接(明码:111111)、反对指定内容导出、在分享第三方的时候会十分的有用因为HTML领有十分不错的UI体现能力,咱们破费了相当多的精力在动态HTML上。咱们举荐应用HTML作为首选的离线文档。 【按需导出】 回收站和日志性能 日志性能是团队治理和平安操作中十分重要的一环,工具对接口的每一步操作都做具体记录。 ...

June 4, 2021 · 1 min · jiezi

关于restful:针对-Restful-协议下的接口测试平台设计

利用背景 目前市场上很多 Web 利用转向了 RESTful 的架构,往往裸露给用户的往往就是一组 REST API,这样的益处就是,研发人员能够依据须要调用不同的 API,整合出本人的利用进去。 这样每组 API 就会造成一个信息中心,各个信息中心联合在一起,就造成了一个互联互通的信息架构。所以针对此种轻量级的风行架构,接口服务的场景测试必不可少,目前支流的 postman 或者 jmeter 之类的工具尽管也能够胜任,然而对于整体设计来说总是欠缺一些什么。 像阿里巴巴之类的大厂始终在推举本人自定义去做一些品质平台,有针对性的去设计适宜本人产品的测试计划,这里存在一个开源的接口服务框架,能够反对 restful 协定,并能够反对 xml、json 之类的数据格式的传输、验证、断言等。 REST Assured 是一套由 Java 实现的 REST API 测试框架,它是一个轻量级的 REST API 客户端,能够间接编写代码向服务器端发动 HTTP 申请,并验证返回后果;它的语法十分简洁,是一种专为测试 REST API 而设计的 DSL。 官网地址: https://rest-assured.io/官网文档: https://rest-assured.io/#docsGithub我的项目地址: https://github.com/rest-assur... 简略的实际 如果在 IDE 配置一个简略些的接口测试环境,那咱们首先能够将 REST Assured 配置到 Maven 中: 配置好后,咱们来看一个典型的测试案例: 在 src/test/java 下创立一个包,起名叫 OurTest,在该包下创立一个 GioApiTests 类 接下来咱们来看一下代码: public class OurTest{@Testpublic void getAddress(){given() requestApplication.get(s:”https://ip:8090/trueTest/com/ourname/226514”) Response .then()ValidateReponse.statusCode(200);}@Testpublic void postOurName(){String json=”{“\firstName\“:\”wangtao\”,\”secondName\”:\”tao\”}”;given() RequestApplication.contentType(“application/json“)RequestApplication.body(json)RequestApplication.post(s:”ip:8090/trueTest/com/ourname”)response.then()ValidateReponse.statusCode(200);}}上面咱们来解释一下: ...

May 17, 2021 · 2 min · jiezi

关于restful:DjangoRestFramework框架简介及基本使用

本文首发于:行者AI在python我的项目开发中,前后端拆散的技术框架越来越成熟,在前后端进行通信时,通常须要用对立的格局进行通信,目前利用比拟宽泛的是RESTful API。那后端如何疾速编写基于Django的RESTful API呢?本篇将次要介绍应用DjangoRestFramework(drf)框架来疾速开发合乎REST格调的API。 1. drf概念及特点1.1 概念drf框架是基于Django框架,用于疾速构建Web RESTful API的工具。 1.2 特点(1) 提供了定义序列化器Serializer的办法,能够疾速依据Django ORM 或者其余库主动序列化、反序列化; (2) 提供了丰盛的类视图、MIXIN扩大类,依据需要组合继承,简化视图的编写; (3) 丰盛的定制层级:函数视图、类视图、视图汇合到主动生成 API,满足各种须要; (4) 反对多种身份认证和权限认证形式; (5) 内置了限流零碎; (6) 可视化API接口; (7) 可扩展性 , 插件丰盛。 2. drf的应用drf对代码的简化次要是对视图的增删改查、申请数据的反序列化和响应数据的序列化进行简化,所以次要介绍drf的序列化器和视图集的应用。 2.1 搭建我的项目搭建我的项目环境,创立我的项目exercise,创立app利用student,代码如下: # python==3.6.5virtualenv -p /python/python.exe /virtualenv/exerciseenvcd /virtualenv/exerciseenv/Scripts/activatepip install django==3.1.5 pymysql==1.0.2 djangorestframework==3.12.2cd /study/django-admin startproject exercisecd exercisedjango-admin startapp student目录构造如下: 而后关上我的项目在/exercise/settings.py文件中配置数据库,注册app(student和rest_framework)。 2.2 创立模型在/student/models.py文件中,建设三张表:班级(Grade)、课程(Course)、学生(Student)。 class Grade(models.Model): # 班级 name = models.CharField(max_length=16, null=False, unique=True) class Meta: db_table = 'grade' ordering = ['id']class Course(models.Model): # 课程 name = models.CharField(max_length=32, unique=True, null=False) class Meta: db_table = 'course' ordering = ['id']class Student(models.Model): # 学生 name = models.CharField(max_length=16, null=False) age = models.IntegerField(null=True) gender = models.BooleanField(null=False, default=0) g = models.ForeignKey(Grade, on_delete=models.CASCADE) c = models.ManyToManyField(Course) class Meta: db_table = 'student' ordering = ['id']2.3 创立序列化器在/student/serializers.py文件中,建设三个模型对应的序列化器;序列化器有两个次要性能:序列化和反序列化。如果前端是GET申请,则结构查问集,将后果返回,这个过程为序列化;如果前端是POST申请,要对数据库进行改变,则须要拿到前端发来的数据,进行校验,校验通过将数据写入数据库,这个过程称为反序列化。这能极大的简化视图代码量,前面会做个比照。代码如下: ...

January 28, 2021 · 3 min · jiezi

关于restful:RESTful-API如何进行版本控制

本文将帮忙您了解为什么须要版本控制,以及如何对REST API进行版本控制。咱们将探讨4种版本控制的办法,并比拟不同的办法。 您将学到 为什么咱们须要对RESTful API 进行版本控制?可用的版本控制有哪些?如何实现基于 Restful 的版本控制?为什么咱们须要对RESTful API进行版本化最好的版本控制办法是不进行版本控制。只有不须要版本控制,就不要版本控制。 构建向后兼容的服务,以便尽可能防止版本控制!然而,在许多状况下咱们都须要进行版本控制,然咱们看看上面具体的例子: 最后,你有个这个版本的Student服务,返回数据如下: { "name": "Bob Charlie"}起初,您心愿将学生的名字拆分,因而创立了这个版本的服务。 { "name": { "firstName": "Bob", "lastName": "Charlie" }}您能够从同一个服务反对这两个申请,然而随着每个版本的需要多样化,它会变得越来越简单。 在这种状况下,版本控制就成必不可少,强制性的了。 接下来让咱们创立一个简略的SpringBoot的maven我的项目,并了解对 RESTful 服务进行版本控制的4种不同办法。 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies>几个用于实现版本控制的Bean第一个版本的 Bean @Data@AllArgsConstructorpublic class StudentV1 { private String name;}第二个版本的 Bean @Datapublic class StudentV2 { private Name name;}StudentV2应用的Name实体 @Data@AllArgsConstructorpublic class Name { private String firstName; private String lastName;}Restful 版本控制的办法咱们心愿创立两个版本的服务,一个返回 StudentV1,另一个返回 StudentV2。 ...

January 26, 2021 · 2 min · jiezi

关于restful:restful风格请求基于token鉴权实例

点赞再看,养成习惯 开发环境:jdk 8intellij ideamaven 3.6所用技术:springbootrestful我的项目介绍基于restful格调做的设计实例,即可jwt做token效验,实现增删查改,同时搭配自定义注解,不便过滤token验证 自定义注解1.须要做验证的注解 @Target({ElementType.METHOD, ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface UserLoginToken { boolean required() default true;}//拦挡类(AuthenticationInterceptor)代码public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception { String token = httpServletRequest.getHeader("token");// 从 http 申请头中取出 token // 如果不是映射到办法间接通过 if(!(object instanceof HandlerMethod)){ return true; } HandlerMethod handlerMethod=(HandlerMethod)object; Method method=handlerMethod.getMethod(); //查看是否有passtoken正文,有则跳过认证 if (method.isAnnotationPresent(PassToken.class)) { PassToken passToken = method.getAnnotation(PassToken.class); if (passToken.required()) { return true; } } //查看有没有须要用户权限的注解 if (method.isAnnotationPresent(UserLoginToken.class)) { UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class); if (userLoginToken.required()) { // 执行认证 if (token == null) { throw new RuntimeException("无token,请从新登录"); } // 获取 token 中的 user id String userId; try { userId = JWT.decode(token).getAudience().get(0); } catch (JWTDecodeException j) { throw new RuntimeException("token error"); } String user = jedis.get(userId); if (user == null) { throw new RuntimeException("用户不存在,请从新登录"); } // 验证 token JSONObject jsonObject1=JSONObject.parseObject(user); JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jsonObject1.getString("planType"))).build(); try { jwtVerifier.verify(token); } catch (JWTVerificationException e) { throw new RuntimeException("token error"); } return true; } } return true;}我的项目构造申请列表图片 ...

January 5, 2021 · 2 min · jiezi

关于restful:你知道什么是-Restful-风格吗SpringMVC-带我们实现它

你晓得什么是 Restful 格调吗?SpringMVC 带咱们实现它!Restful 格调的 API 是一种软件架构格调,设计格调而不是规范,只是提供了一组设计准则和约束条件。它次要用于客户端和服务器交互类的软件。基于这个格调设计的软件能够更简洁,更有档次,更易于实现缓存等机制。 在 Restful 格调中,用户申请的 url 应用同一个 url 而用申请形式:get,post,delete,put…等形式对申请的解决办法进行辨别,这样能够在前后台分离式的开发中使得前端开发人员不会对申请的资源地址产生混同和大量的查看办法名的麻烦,造成一个对立的接口。 SpringMVC Restful 格调 url 配置实现的形式SpringMVC 的 resturl 是通过 @RequestMapping 及 @PathVariable annotation 提供的,通过如 @RequestMapping(value="/blog /{id}",method=RequestMethod.DELETE) 即可解决 /blog/1 的 delete 申请。 GET(SELECT):从服务器查问,能够在服务器通过申请的参数辨别查问的 形式。POST(CREATE):在服务器端新建一个资源,调用 insert 操作。PUT(UPDATE):在服务器端更新资源,调用 update 操作。PATCH(UPDATE):在服务器端更新资源(客户端提供扭转的属性)。(目前 jdk7 未实现,tomcat7 不反对)。DELETE(DELETE):从服务器端删除资源,调用 delete 语句。案例实操Get 申请配置/***restful-->get 申请 执行查问操作* @param id* @return*/@RequestMapping(value="queryAccountById02/{id}",method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic MessageModel queryAccountById(@PathVariable Integer id){ MessageModel messageModel=new MessageModel(); if(null==id){ messageModel.setCode(300); messageModel.setMsg("参数非法!"); return messageModel; } messageModel.setResult(accountService.queryById(id)); return messageModel; } Post 申请配置/*** restful-->post 申请执行增加操作* @param id* @param aname* @return*/@RequestMapping(value="saveAccount",method=RequestMethod.POST,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic MessageModel queryAccountById04(@RequestBody Account account){ MessageModel messageModel=new MessageModel(); try { accountService.saveOrUpdateAccount(account); } catch (ParamsException e) { e.printStackTrace(); messageModel.setCode(e.getErrorCode()); messageModel.setMsg(e.getErrorMsg()); }catch (Exception e) { e.printStackTrace(); messageModel.setCode(300); messageModel.setMsg("操作失败!"); } return messageModel; } Put 申请配置/*** restful-->put 申请执行更新操作* @param id* @param account* @return*/@RequestMapping(value="update/{id}",method=RequestMethod.PUT,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic MessageModel queryAccountById04(@PathVariable Integer id,@RequestBody Account account){ MessageModel messageModel=new MessageModel(); try { accountService.saveOrUpdateAccount(account); } catch (ParamsException e) { e.printStackTrace(); messageModel.setCode(e.getErrorCode()); messageModel.setMsg(e.getErrorMsg()); }catch (Exception e) { e.printStackTrace(); messageModel.setCode(300); messageModel.setMsg("操作失败!"); } return messageModel; } Delete 申请配置 /** * restful-->delete 申请 执行删除操作 * @param id * @return */@RequestMapping(value="deleteAccountById/{id}",method=RequestMethod.DELETE,produces=MediaType.APPLICATION_JSON_UTF8_VALUE)@ResponseBodypublic MessageModel queryAccountById05(@PathVariable Integer id){ MessageModel messageModel=new MessageModel(); try { accountService.deleteAccountById(id); } catch (ParamsException e) { e.printStackTrace(); messageModel.setCode(e.getErrorCode()); messageModel.setMsg(e.getErrorMsg()); }catch (Exception e) { e.printStackTrace(); messageModel.setCode(300); messageModel.setMsg("操作失败!"); } return messageModel;} 扩大~RESTREST(英文:Representational State Transfer,简称REST)形容了一个架构款式的网络系统,比方 web 应用程序。它首次呈现在 2000 年 Roy Fielding 的博士论文中,Roy Fielding是 HTTP 标准的次要编写者之一。在目前支流的三种Web服务交互计划中,REST相比于SOAP(Simple Object Access protocol,简略对象拜访协定)以及XML-RPC更加简单明了,无论是对URL的解决还是对Payload的编码,REST都偏向于用更加简略轻量的办法设计和实现。值得注意的是REST并没有一个明确的规范,而更像是一种设计的格调。 ...

December 10, 2020 · 1 min · jiezi

关于restful:restFul风格两种实现方式

restFul格调实现1作用:能够动静的接管url中的地址。语法: 1.url中的地址如果是参数,则须要应用/宰割2.controller办法接管参数时,须要使{}号来获取3.如果须要获取参数信息,则应用特定的注解标识@RequestMapping("/page/{moduleName}")public String module(@PathVariable String moduleName){ return moduleName; }restFul格调实现2须要指定拜访申请类型,并且依据特定的类型执行业务。那么,什么叫指定拜访申请类型? 申请类型常见的有多少种? 1.post 执行入库操作2.get 执行查问的操作3.put 执行更新操作4.delete 执行删除操作@RequestMapping(value = "/page/{moduleName}",method = RequestMethod.GET) public String module(@PathVariable String moduleName){ return moduleName; }简化restFul格调实现2的代码 @GetMapping("/page/{moduleName}")public String module(@PathVariable String moduleName){ return moduleName;}

November 6, 2020 · 1 min · jiezi

关于restful:从零开始搭建完整的电影全栈系统三restfulApi的编写

创立API利用入口:1,复制 backend ⾄ api, environments/dev/backend ⾄ environments/dev/api 以及 environments/prod/backend ⾄ environments/prod/api.2,批改配置⽂件main.php,须要批改应⽤id、命名空间、⽤户组件和url丑化的配置内容 'id' => 'app-api','controllerNamespace' => 'api\controllers',3,在 commonconfigbootstrap.php 中增加api的门路别名 Yii::setAlias('@api', dirname(dirname(__DIR__)) . '/api');独自创立api入口的益处:独自创立API应⽤,⽬的是便于保护。能够防止这些问题:• 配置的抵触 • 控制器的命名不便 • url丑化规定抵触 创立第⼀个API应⽤:1,创立了vod-detail控制器:VodDetailController.php <?phpnamespace api\controllers;use Yii;use common\models\VodDetail;use common\models\VodDetailSearch;use yii\rest\ActiveController;use yii\web\Controller;use yii\web\NotFoundHttpException;use yii\filters\VerbFilter;/** * VodDetailController implements the CRUD actions for VodDetail model. */class VodDetailController extends ActiveController{ public $modelClass = 'common\models\VodDetail';}2,在配置⽂件中,创立了针对vod-detail管制器的url丑化规定:main.php: 'urlManager' => [ 'enablePrettyUrl' => true, //严格解析 至多要合乎rules中的一条,否则抛出异样 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => 'vod-detail', ], ], ],3,配置main-local⽂件,让API应⽤能接管 JSON 格局的输⼊数据:main-local.php: ...

September 10, 2020 · 1 min · jiezi

关于restful:restful风格

平时跳转页面并传递参数时,咱们能够通过url?拼接参数的形式来进行,还能够通过restful来实现跳转. 定义RESTFUL是一种网络应用程序的设计格调和开发方式,基于HTTP能够应用XML格局定义或JSON格局定义 利用场景当用户发动申请时,其中有多个申请都是相似的性能时(例如:只是跳转页面),是否用一个controller来实现通用的跳转? 实现就须要用restful格调来动静的接管url中的参数restful格调实现: 参数与参数之间由"/"分隔参数应用{}模式包裹controller类参数@PathVarible实现数据的转化总结如果须要获取url地址中的参数时,则能够应用restful格调实现须要依照类型执行特定的性能(type="get"--查/"post"--增...)跳转.@Controllerpublic class IndexController { /** * 对于通用页面跳转的阐明 * url地址: /page/item-add * url地址: /page/item-list * url地址: /page/item-param-list * 依照惯例: 1个申请对应的1个controller办法 * 需要: 是否利用一个办法履行页面的通用的跳转. * 想法: 是否动静的接管url中的参数呢?? * * restFul格调实现1: * 1.参数与参数之间应用/分隔 * 2.参数应用{}模式包裹 * 3.@PathVariable 实现数据的转化. * * restFul格调实现2: * 能够利用申请的类型,指定业务性能. * TYPE="GET" 查问业务 * TYPE="POST" 新增业务 * TYPE="PUT" 更新业务 * TYPE="DELETE" 删除业务 * * 总结1: 如果须要获取url地址中的参数时,则能够应用RestFul格调实现. * 总结2: 能够依照类型执行特定的性能. */ //@RequestMapping(value = "/page/{moduleName}",method = RequestMethod.GET) @GetMapping("/page/{moduleName}") public String itemAdd(@PathVariable String moduleName){ //目标:跳转页面 item-add return moduleName; }}

August 29, 2020 · 1 min · jiezi

关于restful:开发出优秀的API构建RESTful-API的13种最佳实践

首发于公众号《前端全栈开发者》,第一工夫浏览最新文章,会优先两天发表新文章。Facebook、GitHub、Google以及其余许多巨头都须要一种服务和生产数据的形式。在当今的开发环境中,RESTful API依然是服务和生产数据的最佳抉择之一。 然而你是否思考过学习行业标准?设计RESTful API的最佳实际是什么?从实践上讲,任何人都能够在不到五分钟的工夫内疾速启动数据API——无论是Node.js,Golang还是Python。 咱们将探讨在构建RESTful API时应思考的13种最佳实际。但首先,让咱们疾速说明RESTful API。 什么是RESTful API?RESTful API须要满足以下束缚能力被称为RESTful API。 客户端-服务器模型:RESTful API遵循客户端-服务器模型,其中服务器为数据提供服务,而客户端连贯到服务器以应用数据。客户端和服务器之间的交互是通过HTTP(S)申请进行的,该申请传输了申请的数据。无状态:更重要的是,RESTful API应该是无状态的。每个申请都被视为独立申请。服务器不应跟踪可能影响未来申请后果的任何外部状态。对立接口:最初,一致性定义了客户端和服务器之间的交互方式。RESTful API定义了命名资源的最佳实际,但定义了容许你批改资源/与之交互的固定HTTP操作。能够在RESTful API中拜访以下HTTP操作: GET申请:检索资源POST申请:创立资源或将信息发送到APIPUT申请:创立或替换资源PATCH申请:更新现有资源DELETE申请:删除资源在对RESTful API的个性有了更深刻的理解后,是时候理解更多对于RESTful API的最佳实际了。 本文为你提供了13种最佳实际的可行清单。让咱们来摸索! 1.正确应用HTTP办法咱们曾经探讨了可用于批改资源的HTTP办法:GET,POST,PUT,PATCH和DELETE。 尽管如此,许多开发人员还是偏向于滥用GET和POST或PUT和PATCH。通常,咱们看到开发人员应用POST申请来检索数据。此外,咱们看到开发人员应用PUT申请来替换资源,而他们只想更新该资源的单个字段。 确保应用正确的HTTP办法,因为这将为应用你的RESTful API的开发人员减少很多凌乱。最好是保持应用预约的准则。 2.命名约定理解RESTful API命名约定将对你有组织地设计API有很大帮忙。依据你服务的资源设计一个RESTful API。 例如,你的API治理着作者和书籍(是的,一个经典的例子)。当初,咱们要增加一个新作者或拜访一个ID为 3 的作者。你能够设计上面的路由来达到这个目标: api.com/addNewAuthorapi.com/getAuthorByID/3设想一下,一个API承载了许多资源,每个资源都有许多属性。可能的端点列表将变得无穷无尽,而且对用户不是很敌对。所以咱们须要一种更有条理和标准化的形式来设计API端点。 RESTful API最佳实际形容了端点应以资源名称结尾,而HTTP操作则形容操作。当初咱们失去: POST api.com/authorsGET api.com/authors/3如果咱们想拜访ID为 3 的作者已经写过的所有书籍怎么办?对于这种状况,RESTful API也有解决办法: GET api.com/authors/3/books最初,如果您要为ID为 3 的作者删除ID为 5 的书,该怎么办?同样,让咱们遵循雷同的结构化办法来造成以下端点: DELETE api.com/authors/3/books/5简而言之,利用HTTP操作和资源映射的结构化形式来造成易于了解的端点门路。这种办法的最大长处是,每个开发人员都理解RESTful API的设计形式,他们能够立刻应用API,而不用浏览你的每个端点的文档。 3.应用复数资源资源应始终应用其复数模式。为什么?假如你要检索所有作者。因而,你将调用以下端点:GET api.com/authors。 当你读取申请时,你无奈判断API响应是否只蕴含一个或所有作者。因而,API端点应该应用复数资源。 4.正确应用状态码状态码在这里不只是为了好玩,它们有一个明确的目标,状态码告诉客户端申请的胜利。 最常见的状态码类别包含: 200(OK):申请已胜利解决并实现。201(Created):批示胜利创立资源。400(Bad Request):代表客户端谬误。也就是说,申请的格局不正确或短少申请参数。401(Unauthorized):未受权,你尝试拜访你没有权限的资源。404(Not Found):申请的资源不存在。500(Internal Server Error):外部服务器谬误,服务器在执行申请期间引发异样。状态码的残缺列表能够在Mozilla Developers找到。 5.遵循雷同约定最常见的是,RESTful API提供JSON数据,因而,应遵循camelCase大小写常规。然而,不同的编程语言应用不同的命名约定。 6.如何解决搜寻,分页,过滤和排序搜寻,分页,过滤和排序等操作并不代表独自的端点。这些操作能够通过应用随API申请提供的查问参数来实现。 例如,让咱们检索按名称升序排列的所有作者。你的API申请应如下所示:api.com/authors?sort=name_asc。 此外,我想检索一个名称为“ Michiel”的作者。该申请看起来像这样 api.com/authors?search=Michiel。 ...

August 27, 2020 · 1 min · jiezi

restful规范

Restful全称是Representational State Transfer,即表现层状态转移。REST指的是一组架构约束条件和原则,如果一个架构符合REST的约束条件和原则,就称之为RESTful架构。RESTful是一种软件架构风格,而不是标准。那么表现层状态转移怎么理解?外国人取名总是这么抽象,难以理解。 1、资源REST的名称"表现层状态转化"中,省略了主语。"表现层"其实指的是"资源"(Resources)的"表现层"。所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。 2、表现层"资源"是一种信息实体,它可以有多种外在表现形式。我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)。比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的".html"后缀名是不必要的,因为这个后缀名表示格式,属于"表现层"范畴,而URI应该只代表"资源"的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对"表现层"的描述。 3、状态转化访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。综合上面的解释,我们总结一下什么是RESTful架构:(1)每一个URI代表一种资源;(2)客户端和服务器之间,传递这种资源的某种表现层;(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。总之,一句话描述就是用 URL定位资源,用HTTP动词(GET,POST,DELETE,PUT)描述操作。 参考链接:http://www.ruanyifeng.com/blog/2011/09/restful.html https://www.toutiao.com/i6746910619393327629/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1571272396&app=news_article&utm_source=weixin&utm_medium=toutiao_android&req_id=201910170833160100260772133F90C589&group_id=6746910619393327629

October 17, 2019 · 1 min · jiezi

云端的ABAP-Restful服务开发

愉快的暑假结束了,今天是小朋友新学期开学后的第一个周日,不知道各位家长是否和小朋友们一起,已经适应了新学期的生活了么?Jerry从少的可怜的属于自己的周末时光挤了一小部分时间出来,写了这篇文章。 Jerry之前的一篇文章 从ABAP Netweaver的SICF到SAP Kyma的Lambda Function,我曾经提到过,如果想将ABAP Netweaver里的资源以Restful API的方式暴露出来,SICF这个事务码绝对是一大利器。 我们只需要在SICF里合适的路径下创建节点,为该节点创建一个ABAP类,就可以专注于实现接口定义的方法IF_HTTP_EXTENSION~HANDLE_REQUEST, 在里面编写应用逻辑了。应用开发人员无需关注和操心这些ABAP类什么时候被实例化和调用,可以把这个HANDLE_REQUEST方法看作是一个回调函数,当请求到来时,由Netweaver的ICF框架(Internet Communication Framework)负责把请求路由到对应的ICF节点并创建ABAP类,调用HANDLE_REQUEST方法。每个SICF节点会根据其路径被分配一个url, 如果是Corporate网络里,用浏览器或者编程语言直接访问该url,就能消费SICF暴露的资源了。如果想让这个位于Corporate网络内的url被Internet网络访问,就得借助SAP Cloud Connector: 具体步骤在我之前的文章使用Java+SAP云平台+SAP Cloud Connector调用ABAP On-Premise系统里的函数 里介绍过。 对于ABAP开发人员来说,一个好消息是,SAP Cloud Platform如今也支持ABAP运行环境了。现在我们通过在SAP云平台 ABAP运行环境里完成类似之前在On-Premises ABAP系统的SICF事务码里的开发任务,来感受ABAP到了云端之后,给ABAP开发者带来的巨大便利。 按照Jerry之前的文章在SAP云平台ABAP编程环境上编写第一段ABAP程序 里介绍的步骤,通过ABAP Development Tools连接SAP云平台 ABAP运行环境的一个实例,完成登录后,后续的操作步骤,同使用ABAP Development Tools连接一个On-Premises ABAP系统,几乎没有差别。 下图是在云上的ABAP环境里,允许我们创建的ABAP对象列表,既有ABAP开发人员感到亲切的ABAP字段对象,ABAP开发包,ABAP消息类等等,也有上了云端之后的新面孔,比如Cloud Communication Management和Cloud IAM等。为了完成在On-Premises的SICF事务码里的开发工作,现在我们要在云上的ABAP环境里创建一个新的HTTP Service: 新建一个名为ZHELLOWORLD的service,创建完毕之后界面如下: ABAP老司机们看到这界面,立即知道下一步怎么做了吧。点击Handler class,就可以进入ABAP类的编辑界面,实现这个HTTP服务的业务逻辑。而通过url字段里维护的值,我们可以在PC或移动设备里,浏览器或代码里访问这个服务。该服务实现类和On-Premises的区别,不过是接口名称从IF_HTTP_EXTENSION换成了IF_HTTP_SERVICE_EXTENSION. 当然,前者因为是直接在云端编写的ABAP代码,所以还要遵循Jerry之前的文章 在SAP云平台ABAP编程环境上编写第一段ABAP程序 里提到的那些限制。 上述的ABAP代码只是简单地返回给消费者一个Hello World的文本信息,保存激活之后,把url贴到浏览器里,就能看到期望的Hello World: 我们从url里,容易得出这样的结论,SAP Cloud Platform ABAP运行环境里支持创建的HTTP服务,其实现原理,其实就是在Netweaver服务器的SICF路径/sap/bc/http/sap/下面增加一个新的节点罢了,只是这个操作,在云端不再需要由ABAP开发人员手动完成,云端的ABAP环境,会自动创建这一底层设施。希望传统ABAP开发人员,能从这个最简单的Hello World级别的例子,体会到云端ABAP开发的便利之处。感谢阅读。 要获取更多Jerry的原创文章,请关注公众号"汪子熙":

October 14, 2019 · 1 min · jiezi

Postman的简单使用

一、Postman的简单介绍熟悉HTTP协议,能容易的配置一个简单的http请求,这里就不介绍了。下面是Postman的主要功能: 1、支持定义http请求分组2、支持配置不同环境变量3、支持路径参数、请求头参数、请求实体参数的录入4、支持编写脚本预处理请求(Pre-request Script)5、支持编写脚本断言响应结果及其他测试(Tests) 二、Postman的变量1、作用Postman用于注入请求地址、请求参数、请求实体、请求头中占位符{{}}中的值。 2、分类postman的变量配置,变量类型分为一下3种。每一个变量包含key值,初始化值,当前值三个可编辑项。 变量类型作用范围占位符取值优先级脚本编辑导入导出普通变量当前单个请求最高支持不支持环境变量启用下的所有请求中支持支持全局变量所有请求最低支持支持3、设置入口3.1、可视化设置 3.2、编码设置见变量操作语法 三、Postman的脚本执行1、脚本设置入口1.1、分类以请求为导线,分为Pre-request Script和Tests。以请求组合分类为导线,分为collection级别、folder级别和request级别。以上两种分类的组合共有6个脚本设置入口。 1.2、设置入口1.2.1、collection级别 1.2.1、folder级别 1.2.1、request级别 1.3、执行时间1、Pre-request Script在请求前执行2、Tests在响应后执行3、collection级别在folder级别前执行,folder级别在request级别前执行时间线(上先下后顺序)l类别请求接口前Pre-request Script(collection级别)请求接口前Pre-request Script(folder级别)请求接口前Pre-request Script(request级别)请求接口 请求返回后Tests(collection级别)请求返回后Tests(folder级别)请求返回后Tests(request级别)四、Postman脚本常用语法1、变量操作<div id="变量操作语法"></div> 变量类型set值语法get值语法清除语法普通变量pm.variables.set("key", "value")pm.variables.get("key")pm.variables.unset("variable_key")环境变量pm.environment.set("key", "value")pm.environment.get("key")pm.environment.unset("variable_key")全局变量pm.globals.set("key", "value")pm.globals.get("key")pm.globals.unset("variable_key")2、异步发送非当前请求异步发送Get请求https://postman-echo.com/get pm.sendRequest("https://postman-echo.com/get", function (err, response) { console.log(response.json());});3、响应断言3.1、请求结果断言根据HTTP状态码断言 pm.test("Status code is 200", function () { pm.response.to.have.status(200);});pm.test("Successful POST request", function () { pm.expect(pm.response.code).to.be.oneOf([201,202]);});根据响应状态描述文字断言 pm.test("Status code name has string", function () { pm.response.to.have.status("Created");});3.2、文本响应实体断言断言文本响应结果包含某个字符串 pm.test("Body matches string", function () { pm.expect(pm.response.text()).to.include("string_you_want_to_search");});断言文本响应实体匹配字符串 pm.test("Body is correct", function () { pm.response.to.have.body("response_body_string");});3.3、JSON响应实体断言pm.test("Your test name", function () { var jsonData = pm.response.json(); pm.expect(jsonData.value).to.eql(100);});3.4、响应头断言pm.test("Content-Type is present", function () { pm.response.to.have.header("Content-Type");});3.5、响应耗时断言pm.test("Response time is less than 200ms", function () { pm.expect(pm.response.responseTime).to.be.below(200);});4、响应实体由XML转JSONvar jsonObject = xml2Json(responseBody);5、自定义JSON校验规则var schema = { "items": { "type": "boolean" }};var data1 = [true, false];var data2 = [true, 123];pm.test('Schema is valid', function() { pm.expect(tv4.validate(data1, schema)).to.be.true; pm.expect(tv4.validate(data2, schema)).to.be.true;});6、参考文档Postman Sandbox API reference ...

October 5, 2019 · 1 min · jiezi

随时发布REST-API文档的代码仓库中的持续集成与协作

本文主要内容:API文档提供了预测客户成功的关键路径;在代码附近的文档上进行协作可以更好地检查代码和文档文件,提高自动化效率,并专门针对文档进行质量测试;提供通用文档框架,标准,自动化和工具,以提高团队效率。编写文档有时候会非常枯燥乏味,但优秀的文档是增加API被采用的一个很好的前提。编写出色的文档与编写代码一样需要严谨。随着API的质量逐渐成为产品增长的指标,您的文档比以往任何时候都更加重要,优秀的文档很大程度上代表创建了成功的API。API定义和文档常常结合在一起,虽然今天的API规范越来越多地作为GitHub中的代码进行管理,但API文档并非如此。所以需要让我们对此进行更改,以便在GitHub和相关代码库中编写和管理API文档(包括相关网站)的标准。 通过尽可能靠近代码和API定义协作编写文档,您可以自动执行文档测试,构建和部署。API的文档通常构建为网站,因此如果在分段构建期间就可以进行链接检查等测试。这种方法提供了许多好处:经过测试的文档构建;支持连续发布;支持对文档内容和代码的评论;多个输出(包括经过测试的代码示例);文档的发布管理(如API的早期访问版本)或增量更改和添加到发布了API,并防止代码合并。 文档持续集成/交付对于REST API文档,三个框架以网页的形式提供文档输出,其中许多是交互式的。这些是OpenAPI(Swagger),RESTful API建模语言(RAML)和API蓝图。OpenAPI(以前称为Swagger)基于JSON输出,由于YAML是JSON的超集,因此您可以交换描述API的源文件。RAML是一种基于YAML的语言,用于描述REST API。API Blueprint使用Markdown,也可以遵循GitHub Flavored Markdown语法。 许多编程语言都有一个文档框架,可以很好地与代码本身集成。通常,这些是静态站点生成器,例如Jekyll for Ruby和Sphinx for Python。文档的源文件是Markdown或reStructured Text(RST)。使用这些静态站点生成器,您可以创建漂亮的文档站点。事实上,GitHub上有一系列链接到漂亮的文档网站,其中包括Stripe API文档站点和Basho文档 - 这些是获得美观和实用程序最高分的示例API站点。 由于可以使用脚本转换这些文档源文件和网站配置文件,因此您可以使用与代码相同的构建作业来持续构建文档。通过从静态目录复制平面文件来部署Jekyll站点,因此您可以存储脚本以使用其他构建文件在代码存储库中构建站点。您还可以使用简单的链接检查程序在部署站点之前测试任何损坏的链接。HTMLProofer库是一个为此而编写的Ruby库。 GitHub提供的部署机制称为GitHub Pages。您可以选择触发文档网站部署。您可以从gh-pages分支,主分支自动化构建,或始终从主分支上的/ docs目录部署文档。虽然您可以部署到自定义域名,但GitHub页面的一个限制是您不能直接通过HTTPS提供服务,但作为免费服务,它是一个很好的起点。对于生产网站,您可以从GitHub部署到具有所需安全要求的云服务器或VPS。而GitHub Enterprise是GitHub的内部部署,用于内部部署,提供类似的托管站点功能。 为什么要像代码一样处理文档当成果已经可以交付给开发人员时,那与开发人员一起写文档会很有效。API文档任务为处理代码等文档的原因和时间提供了一个很好的示例。特别是在设计API或向API添加功能时的关键设计点,开发人员可以像文档代码一样贡献文档。 例如,在自动化参考信息时,您可以获得即时性和准确性,并通过在GitHub,Bitbucket或GitLab等社交编码系统中协作编写来获得贡献,质量和同理心。此外,API的最大成功指标之一是文档的准确性和有用性。当您的团队处理代码等API文档时,以下是一些特定的好处,下面将对此进行更深入的探讨: 1.协作效率可以为开发人员和文章协作编写者两个角色提供有价值的信息。开发人员希望为类似于自己的受众撰写文章,为读者提供经验分享。协作编写者有一个特殊的诀窍来组织信息,文笔更好,并以适当的顺序揭示概念和参考信息。如果在同一个存储库中协同工作可以提供沟通的效率,增加教学和被教学的机会。 2.贡献收益当没有专门的技术写作团队时,你可以扩大贡献者的数量,即使API本身不是公共文档的公共文件。或者,您可以通过在GitHub或Bitbucket等版本控制系统中提供开源文档存储库,从最终用户自己获得更多贡献。当文档与代码位于同一存储库中时,任何感兴趣的开发人员(包括客户)都可以订阅拉取请求的通知。 3.跟踪文档中的错误要获得更高质量的文档,请提供直接在输出页面上报告文档错误的方法。正如莱纳斯定律所述,“给予足够的眼球,所有的错误都是浅薄的”。通过为这些眼球提供报告错误的位置,您可以为文档提供更多类似代码的质量跟踪。此外,通过问题跟踪制定的指标,您可以衡量文档的质量,并确保在需要解决这些错误时可使用指标来判断。 4.对文档和代码的评论在查看代码中包含的文档时,审阅者可以在文档不足时阻止对代码的更改。该审查指南为团队提供了使文档具有高质量的能力,即使它们必须与快速变化的代码同步。 将文档源文件放在像GitHub这样的开放系统中可以使内容具有开放贡献,类似于开源代码。在OpenStack中,大量的开源云项目,文档贡献过程与代码贡献过程相同。编写者和开发人员在访问仅文档存储库(仅代码存储库)时具有相同的协作工具和背景知识。 当您的API定义需要一定程度的守门或小心防护时,您还可以考虑将GitHub Enterprise用于内部API文档。您的团队仍然可以在内部获得协作优势,而无需向全世界打开您的文档和代码。 5.部署和发布处理代码等文档时,意识到您正在将构建与部署分离。通过这种方式,您可以对文档的早期草稿进行审核,并且在文档构建部署并发布到网站之前,它都可以随时进行修正。或者,您可以选择不断发布一些文档以跟上代码。例如,您可以选择编写叙述性文档和教程,这些文档和教程会随着每个补丁集添加而不断发布。但对于像OpenAPI规范这样的合同文档,只有在考虑在特定版本下发布API时,才能部署新文档。 6.测试和构建文档通过为评论提供匹配的分段构建和向读者交付的生产构建,您可以确信您对构建的文档的审核满足用户需求。请参阅以下部分中的示例。 docs的示例测试您可以测试文档源文件的质量以及是否构建。您还可以检查拼写或语法,但也可以通过人为进行检查。但测试文档的目的是减轻人类审阅者的审查负担,自动化能节省时间,以便可以编写,发布和发布更多文档文件。 链接检查对于许多静态站点生成器,有专门用于检查站点中所有链接的库。例如,在检查像docslikecode.com这样的Jekyll网站上的链接时,Travis CI工作很简单: #!/usr/bin/env bashset -e # halt script on errorbundle exec jekyll buildbundle exec htmlproofer ./_site通过这种类型的测试,人工审阅者不必花时间点击新补丁中的每个链接。如果需要更新外部链接,那么这个测试的结果会通知您。如果内部链接因修补程序本身而中断,则系统可以在人为查看修补程序之前修复它。 JSON格式使用REST API文档,请求和响应通常是JSON文件,对于阅读文档的人在将自己的环境与文档进行比较时非常有价值。在读者需要复制和粘贴请求的情况下,正确格式化示例至关重要。在OpenStack中,我们有一组包含JSON测试器和格式化程序的通用文档工具。通过对传入的修补程序对文档文件运行此测试,读者可以确定所包含的JSON文件是否格式正确。此外,当它们可以在本地运行简单命令以确保JSON格式正确时,它可以使贡献者更容易创建补丁。 验证的请求和响应高级文档测试可以检查文档中包含的示例请求和响应。用查看代码文件相同的方式查看文档文件时,准确性通常是最高值。因此,通过针对正常工作的API端点测试示例,您可以证明这些示例也适用于实际情况。这种类型的验证提供了每次都可用的成功文档示例,因为只有它们通过验证测试,它们才被发布。 建立检查自动化文档测试可以节省读者的时间,因为他们不必自己构建文档来查看输出。此外您可以测试损坏的链接或不正确格式化的JSON文件的构建。对于任何奇怪的问题,查看源文档和输出文档都很有帮助。 结论在与代码库相同的仓库中协同处理文档,可以更好地为客户提供服务,无论他们是组织的内部还是外部。通过将API文档视为代码,您可以找到自动化途径,提高效率,加快文档构建,在文档中构建成功示例。 自动化测试功能除了能减少开发的时间之外,自动化测试还有助于进行项目流程测试,最近因为一直在使用EOLINKER进行API研发管理,因此推荐自动化测试功能,因为它支持UI模式和高级模式,可以实现不同类型的自动化测试项目,比如登录验证就可以用UI模式,高级模式则用来测试较为复杂的项目,对比之前效率高了不少,对API自动化测试等方面有兴趣的小伙伴前往了解下哦!https://www.eolinker.com 作者:Anne Gentle,思科的技术产品经理; 原文标题:Always Be Publishing: Continuous Integration & Collaboration in Code Repositories for REST API Docs; ...

July 9, 2019 · 1 min · jiezi

分析RESTful-API安全性及如何采取保护措施

本文中讨论了API安全性和采用安全措施的重要性,如身份验证,API密钥,访问控制和输入验证。API设计的第一步是撰写接口文档根据TechTarget(海外IT专业媒体)的定义,RESTful API是一个应用程序接口,它使用HTTP请求来获取GET,PUT,POST和DELETE等数据。从技术层面上看,RESTful API(也称为RESTful Web服务)是一种基于代表性状态转移(REST)技术,这是一种通常用于Web服务开发的架构风格和通信方法。 但随着 RESTful API 应用范围的爆炸性扩大,安全性越来越成为API架构设计中最容易被忽视的部分。 为什么API安全性很重要?在设计和部署RESTful API时,有以下三个核心原因可以解释为什么安全性应该是一个很重要的考虑因素。 1.数据保护RESTful API是一种向外界传输价值的服务方式。因此,保护通过RESTful方式提供的数据始终应该是属于高优先级。 2.DOS攻击如果不采取正确的安全措施,(DOS)攻击可以使RESTful API进入非功能状态。考虑到很多基础的RESTful API是开放给所有人使用的情况,通过这种类似开源的方式有助于它更好推广给市场,让更多人投入使用,但同时意味着如果有人选择对API执行DOS攻击时,它也可能导致灾难性的结果。 3.商业影响如今有越来多的服务平台,提供着影响衣食住行的各种信息,从飞机航班时刻表到高铁余票查询,甚至只是超市里日常用品,都能给你提供价格、数量、时间等诸多信息,让你足不出户,买到最合心意的商品。在这样的大趋势下,这种利用API数据来获取更多信息,再提供给你的聚合服务平台将会越来越多。于是通过RESTful API传输的信息会被频繁调用,而其中的个人信息很容易被泄露。 保障安全采用的措施以下介绍一些RESTful API通用设计中的关键概念。 1.会话管理和认证除了使用TLS / HTTPS之外,最重要的RESTful API安全级别是以会话管理和身份验证为中心的。在本文讨论中重点将放在API密钥,OpenID Connect / OAuth2 / SAML和会话状态事项上。 2.API密钥API密钥的概念是为使用者提供了作为其HTTP请求的一部分的唯一字符串(密钥)。虽然不是一个完整的安全解决方案,但与匿名使用相比,使用API密钥可以更清楚地了解谁在使用API。 API密钥也可用于提供附加服务。例如,对于RESTfulAPI,附加的服务可以选择使用不同的级别。以一般、高、最高三个等级为例,在“一般”的级别,用户可以自由访问,但只能访问一组有限的数据。假如要访问更多组的数据,那就必须支付费用才能访问“高”的级别,以此类推,不受限制的访问所有的数据则要支付“最高”等级的费用,通过提供不同API密钥的方式,提供额外的服务。 使用API密钥的最常用的方式是将它们包含在请求头中。 例如,当调用某个小部件的头部时,67A73DD1BD1D90210BA的API设置为HTTP头部中的X-API-KEY键/值对: curl-H“X-API-键:67A73DD1BD1D90210BA” API密钥的另一个常见用法是将该密钥包含在URI中: https://www.example.com/v1/wi... 67a73dde1bdd1d90210ba 但这种方法的问题在于,API密钥在浏览器历史记录和对应服务器的日志中都会显示出来,意味着向所有有权访问这些数据的人公开唯一密钥。 3.OpenID Connect,OAuth2和SAMLOpenID Connect,OAuth2和SAML使用HTTP协议作为传输,用于安全目的。身份验证提供个人的验证,同时授权执行或撤消访问的任务。 从身份验证角度来看,存在以下选项: OpenID Connect:可以利用现有的身份提供商(如Google或Facebook)获取用于验证用户授权的令牌。OAuth2:可以通过授权执行伪认证(如下所述)。SAML:使用断言、协议、绑定和配置文件来管理身份验证,包括标识提供者,但不太适合移动应用程序验证。为提供授权服务,采用以下策略: OAuth2:提供安全的委托访问,通过允许第三方身份提供程序颁发令牌来代表用户执行操作。由于OAuth2必须了解被委派的用户,因此身份验证是以伪方式实现的(如上所述)。SAML:对可信服务执行断言,包括提供授权的令牌。4.会话状态事项RESTful API端点应始终保持无状态会话状态,这意味着会话的所有内容都必须保存在客户端。来自客户端的每个请求都必须包含服务器理解请求所必需的所有信息。为了简化流程,包括API令牌以及会话令牌,作为每个业务中都需要的一部分。 5.访问控制如上所述,对RESTful服务的授权可以将安全性引入到所提供的端点中,以便对那些可以向API发出HTTP删除请求的人有限制。 在下面的简单Java示例中,只有Admin和Manager角色(即组)的成员可以执行删除所有用户的DELETE请求,但是所有用户都可以执行GET请求以获取用户列表: 6.速率限制如上所述,API密钥是判断RESTful API的使用者身份级别一种很有用的策略。除了提供等级识别外,使用API密钥的另一个好处是能够限制API的使用,例子像Tibco Mashery、MuleSoft和Dell Boomi等API管理解决方案允许限制API请求,利用API密钥实现此功能。因此,试图执行DoS攻击(有意或无意)的时候将会达到一个设定的阈值,到达阈值后后续所有的请求都将被拒绝。 7.输入验证和HTTP返回代码在保护RESTful API时,应始终考虑对输入进行验证。例如,如果用户试图发布与地址相关的JSON数据集合,则RESTful端点内的服务应验证数据并使用HTTP返回代码来反映正确的状态。在下面简化的Java示例中,就是调用一个非常基本的AdvSersService来验证和保存地址: 在上面的示例中,使用isValidAddress()方法验证newAddress对象(从JSON编组到Address Java对象)。如果地址无效,则向用户返回HTTP 401(错误请求)代码,文本显示为“提供的地址无效。” 如果认为该地址有效,则convertAddress()将执行必要的操作,然后将包含地址内容的JSON格式的字符串返回给用户,以及一个HTTP 201(创建)返回代码。 结论保护RESTful API的安全应该始终处于API设计工作中最先被考虑的部分。如果不保护敏感数据,允许DOS攻击以及不考虑使用RESTful API的影响,那么即使它只是短暂的影响,相关的风险可能很容易使企业处于不利地位。 ...

July 1, 2019 · 1 min · jiezi

spring-boot开发soap-webservice

介绍spring boot web模块提供了RestController实现restful,第一次看到这个名字的时候以为还有SoapController,很可惜没有,对于soap webservice提供了另外一个模块spring-boot-starter-web-services支持。本文介绍如何在spring boot中开发soap webservice接口,以及接口如何同时支持soap和restful两种协议。 soap webservice Web service是一个平台独立的,低耦合的,自包含的、基于可编程的web的应用程序,既可以是soap webservice也可以是restwebservice,在rest还没出来之前,我们说webservice一般是指基于soap协议进行通信的web应用程序。  在开始之前,我觉得有必要了解下soap webservice,具体的概念网上可以找到很多资料,但网上资料概念性较强,而且soap协议使用的是xml进行通信,相信xml里面一个namespace就能吓跑一大堆人,所以这里不讨论具体的soap协议细节,我想通过一个例子来说明什么是soap webservice,通过该例子,你能了解soap webservice其运作原理,当然如果你觉得你对这个已经很了解了,大可跳过本章节,本章节跟后面的内容没有任何关系。假设我们开发了一个web接口,想给别人用,我们要怎么办     1.部署接口到服务器         2.编写接口文档,写清楚接口是通过什么方法调的,输入参数是什么,输出参数是什么,错误时返回什么。     那问题来了,我们能不能只把接口部署到服务器上,然后接口不单能提供具体的服务,而且还能自动生成一份标准的接口文档,把接口信息都记录在该文档里,如果能做到,是不是能做到"接口即文档"的目的。      那么一个接口的信息包括哪些呢?     1.接口地址          2.接口调用方法          3.接口输入参数          4.接口输出参数          5.接口出错返回信息          6..... soap webservice里wsdl文件就是接口描述信息。核心的信息就是以上几个。第二个问题,由于Web service是一个平台独立,也就是说,使用接口的人不知道这个service是用什么技术开发的,可能是php可能是java等,但接口的参数和返回的数据都是一样的,要达到这种目的,就需要两个东西,一个是跟平台无关的数据格式,soap使用的是xml,一个是通信协议,也就是soap协议。 下面就介绍如何不使用任何框架,仅通过servlet实现一个webservice。该webservice功能很简单,就是通过一个人的姓名查询这个人的详细信息。 ps:servlet是java web的基础,理解servlet对理解整个java web非常重要,没写过servlet就开始用各种框架写接口就是在胡闹。  1. wsdl文件  准备以下wsdl文件,不要管这个文件是怎么来的,是怎么生成的,我们这次只讲原理,不谈细节,总之,你根据需求写出了这个wsdl文件。 soap:address location里面端口号需要修改为servlet运行的端口号。  从以下xml片段可以看出    接口名称是EmployeeDetail(wsdl:operation)     接口输入参数是EmployeeDetailRequest(wsdl:input)     接口输出参数是EmployeeDetailResponse(wsdl:output)     接口地址是http://localhost:8081/ws-servlet/ws/employee-detail(soap:address)是不是很简单,是的,为了简单,我直接将wsdl文件用变量存储,我们还需要配置下web.xmlweb.xml这样我们访问http://localhost:8080/ws/employee就能返回一个wsdl文件,也就是接口描述文件。在wsdl文件里,我们定义接口地址为http://localhost:8080/ws/employee-detail,接下来我们就要实现这个接口。   业务servlet这里不做任何业务处理,不做xml转bean,不做bean转xml,就是这么暴力,直接返回xml,但他仍是一个soap服务,支持所有soap工具调用。  将servlet配置到web.xml里 web.xml 这个地址必须和wsdl文件里定义的保持一致,不然服务无法被找到。  测试 使用soapui测试我们的webservice,通过地址http://localhost:8081/ws-servlet/ws/employee导入wsdl文件,测试接口,返回我们在业务servlet里面写死的内容。恭喜你,你已经不依赖任何第三方包完成了一个soap webservice。  ...

June 19, 2019 · 1 min · jiezi

正确甄别API-REST-API-RESTful-API-Web-Service之间的差异与联系

看到API你会想起什么?是接口、第三方调用、还是API文档?初看你可能会觉得这太熟悉了,这不是系统开发日常系列吗?但你仔细想一想,你会发现API的概念在你脑海里是如此的模糊。如果你通过搜索引擎检索API,你会看到类似这样的信息:API——Application Programming Interface(应用程序编程接口),这太抽象了。接下来,我将结合在开发中总结的一些经验,以通俗的方式聊聊API、REST API、RESTful API以及Web Service这四者之间的联系与区别。 1、API 与 REST API 什么是API?这里引述维基百科给出的定义:应用程序接口(英语:Application Programming Interface,缩写:API;又称为应用编程接口)是软件系统不同组成部分衔接的约定。这个对API的定义太过于广泛和抽象,而通俗的讲,API是一段应用程序与另一段应用程序相互“交流”的方式(协议)。在Web应用程开发中,API是我们通过网络进行数据检索的一种主要方式,API文档将告知你检索数据的URL列表、查询参数、请求方式以及响应状态,其目的是降低Web应用程序开发难度,共享两个应用程序之间的数据(文本、音频、视频、图片等),而屏蔽其内部复杂的实现细节。 REST是Representational State Transfer的缩写,直译过来就是:表述状态的转移。REST API是一组关于如何构建Web应用程序API的架构规则、标准或指导,或者说REST API是遵循API原则的一种架构风格。REST是专门针对Web应用程序而设计的,其目的在于降低开发的复杂度,提高系统的可伸缩性。下面是设计REST风格的系统架构时需要满足或者遵循的一些基本条件和原则: 1、在REST架构中,Web中所有的事物(文本、音频、视频、图片、链接)都可以被统一的抽象为资源(resource)2、在REST架构中,每一个资源都有与之对应的唯一资源标识符(resource identifier),当资源的状态发生改变时,资源标识符不会发生改变3、在REST架构中,所有的操作都是无状态的。REST架构遵循CRUD原则,所有的资源都可以通过GET、POST、PUT和DELETE这四种行为完成对应的操作。4、可缓存(可选项),在REST架构中需要缓存来有效的处理大批量的请求5、接口一致 现在,了解了API和REST API的基本概念,那这两者之间有什么异同?如果按照数学上集合的概念来解释API与REST API之间的联系与区别,API是REST API的超集,REST API 是API的子集;所有的REST API都是API,但不是所有的API都是REST API。更通俗的解释是:所有的男人都是人,但不是所有的人都是男人。 2、REST API 与RESTful API 在第一小节中,了解了什么是REST API,接下来聊聊REST API与RESTful API之间的异同。很多初学者很容易将这两者等同起来,认为RESTful API就是REST API,这可能是单纯的从字面上去理解了,当你深入的去了解两者的本质后,你会发现其实不然。REST API是Web API设计的一种规范或者指导原则,而RESTful API则是这中架构设计原则或者规范的一种具体实现方式。也就是说,RESTful API是REST API的非正式实现方式,因为实现REST API的方式有很多,RESTful API只是其中一种,且没有完全满足REST API的所有设计原则,每个开发者在实现REST 架构时的则重点都会有差别。 很多初学者容易将REST API与RESTful API两者的概念搞混淆,我想可能只是看字面意思,而没有关注它们本身的含义(就像认识中文字一样,有边读边,无边读中间,断章取义了)。这就好比很多人会把变性人等同于女人,变性人可能五官的表象看起来和女人一样,但变性人不能生育,它只是满足了定义一个女性的大多数条件(实现),但本质上不是女人。 接下来,通过一个简单的例子以加深对REST API和RESTful API的理解。下面将给出一个执行CURD操作的RESTful API设计案例: 说明:resource代指某种资源的名称,可以时student(学生)、teacher(老师)、book(书籍)等等,通常使用名词来表示;{id}则代指某种资源的唯一标识符(resource identifier)下面是一个具体的小例子,以学生管理为例,设计学生管理的API。学生资源包括ID,姓名和所学课程信息,学生资源信息如下: 现在,我们需要将学生数据保存到数据库,然后执行查询、修改和删除学生数据的操作。学生管理API的使用者调用的API如下: 1、创建学生资源:[POST] http://www.example.com/student2、获取所有学生资源:[GET] http://www.example.com/students3、获取ID=1001的学生资源:[GET] http://www.example.com/studen...4、修改ID=1001的学生资源:[PUT] http://www.example.com/studen...5、删除ID=1001的学生资源:[DELETE] http://www.example.com/studen... 前面的内容说到,API共享数据资源,而屏蔽内部实现,API的使用者(客户端)关注的是资源(读懂数据),并不需要了解API内部构造;API的提供者(服务端)只关注自己的内部实现,而不关系API使用者(客户端)的状态。为了加深对这一概念的理解,下面给出学生管理API的内部实现示例: ...

June 17, 2019 · 1 min · jiezi

Spring-Boot-中-10-行代码构建-RESTful-风格应用

RESTful ,到现在相信已经没人不知道这个东西了吧!关于 RESTful 的概念,我这里就不做过多介绍了,传统的 Struts 对 RESTful 支持不够友好 ,但是 SpringMVC 对于 RESTful 提供了很好的支持,常见的相关注解有: <!--more--> @RestController@GetMapping@PutMapping@PostMapping@DeleteMapping@ResponseBody...这些注解都是和 RESTful 相关的,在移动互联网中,RESTful 得到了非常广泛的使用。RESTful 这个概念提出来很早,但是以前没有移动互联网时,我们做的大部分应用都是前后端不分的,在这种架构的应用中,数据基本上都是在后端渲染好返回给前端展示的,此时 RESTful 在 Web 应用中基本就没用武之地,移动互联网的兴起,让我们一套后台对应多个前端项目,因此前后端分离,RESTful 顺利走上前台。 Spring Boot 继承自 Spring + SpringMVC, SpringMVC 中对于 RESTful 支持的特性在 Spring Boot 中全盘接收,同时,结合 Jpa 和 自动化配置,对于 RESTful 还提供了更多的支持,使得开发者几乎不需要写代码(很少几行),就能快速实现一个 RESTful 风格的增删改查。 接下来,松哥通过一个简单的案例,来向大家展示 Spring Boot 对于 RESTful 的支持。 实战创建工程首先创建一个 Spring Boot 工程,引入 Web 、 Jpa 、 MySQL 、Rest Repositories 依赖: 创建完成后,还需要锁定 MySQL 驱动的版本以及加入 Druid 数据库连接池,完整依赖如下: ...

June 6, 2019 · 2 min · jiezi

python-restful实例

test.pyfrom flask import Flask, gfrom flask_restful import reqparse, Api, Resourcefrom flask_httpauth import HTTPTokenAuth# Flask相关变量声明app = Flask(__name__)api = Api(app)# RESTfulAPI的参数解析 -- put / post参数解析parser_put = reqparse.RequestParser()parser_put.add_argument("keyword", type=str, required=True, help="need user data")parser_put.add_argument("lv", type=str, required=True, help="need pwd data")# 功能方法部分案例def to_do(arg1, args2): return str(arg1) + str(args2)# 操作(post / get)资源列表class TodoList(Resource): def post(self): args = parser_put.parse_args() # 构建新参数 keyword = args['keyword'] lv = args['lv'] # 调用方法to_do info = {"info": to_do(keyword, lv)} # 资源添加成功,返回201 return info, 201# 设置路由,即路由地址为http://127.0.0.1:5000/usersapi.add_resource(TodoList, "/users")if __name__ == "__main__": app.run(debug=True)运行如图 ...

May 24, 2019 · 1 min · jiezi

SQLRESTful开源GO脚手架工具ginbrogin-and-gorms-brother-详解

安装felixgit clone https://github.com/dejavuzhou/felixcd felixgo mod downloadgo installecho "添加 GOBIN 到 PATH环境变量"echo "或者"go get github.com/dejavuzhou/felixecho "go build && ./felix -h"What is GinbroGin脚手架工具:因为工作中非常多次的使用mysql数据库 + gin + GORM 来开发RESTful API程序,所以开发一个Go语言的RESTful APIs的脚手架工具Ginbro代码来源:Ginrbo的代码迭代自github.com/dejavuzhou/ginbroSPA二进制化工具:vuejs全家桶代码二进制化成go代码,编译的时候变成二进制,运行的时候直接加载到内存中,同时和gin API在一个域名下不需要再nginx中配置rewrite或者跨域,加快API访问速度功能一:Gin+GORM_SQL RESTful 脚手架工具工作原理通过cobra 获取命令行参数使用sql参数连接数据库后去数据库表的名称和字段类型等数据库数据库边的表名和字段信息,转换成 Swagger doc 规范字段 和 GORM 模型字段使用标准库 text/template 生成swagger.yaml, GORM 模型文件, GIN handler 文件 ...使用 go fmt ./... 格式化代码使用标准库archive/zip打包*.go config.toml ...代码,提供zip文件下载(命令行模式没有)支持数据库大多数SQL数据库mysqlSQLitepostgreSQLmssql(TODO:: sqlserver)ginbro 生成app代码包含功能简介每一张数据库表生成一个RESTful规范的资源(GET<pagination>/POST/GET<one>/PATCH/DELETE)支持API-json数据分页-和总数分页缓存,减少全表扫描支持golang-内存单机缓存缓存前端代码和API公用一个服务,减少跨域OPTION的请求时间和配置时间,同时完美支持前后端分离开箱支持jwt-token认证和Bearer Token 路由中间件开箱即用的logrus数据库开箱即用的viper配置文件开箱即用的swagger API 文档开箱即用的定时任务系统项目演示地址felix sshw 网页UI演示地址 用户名和密码都是admin生成swagger API交互文档地址 http://ginbro.mojotv.cn/swagger/msql生成go代码地址bili命令行演示视频地址命令行参数详解[root@ericzhou felix]# felix ginbro -hgenerate a RESTful APIs app with gin and gorm for gophersUsage: felix ginbro [flags]示例:felix ginbro -a dev.wordpress.com:3306 -P go_package_name -n db_name -u db_username -p 'my_db_password' -d '~/thisDir'Flags: --authColumn string 使用bcrypt方式加密的用户表密码字段名称 (default "password") --authTable string 认知登陆用户表名称 (default "users") -a, --dbAddr string 数据库连接的地址 (default "127.0.0.1:3306") -c, --dbChar string 数据库字符集 (default "utf8") -n, --dbName string 数据库名称 -p, --dbPassword string 数据库密码 (default "password") -t, --dbType string 数据库类型: mysql/postgres/mssql/sqlite (default "mysql") -u, --dbUser string 数据库用户名 (default "root") -d, --dir string golang代码输出的目录,默认是当前目录 (default ".") -h, --help 帮助 -l, --listen string 生成go app 接口监听的地址 (default "127.0.0.1:5555") --pkg string 生成go app 包名称(go version > 1.12) 生成go.mod文件, eg: ginbroSon[root@ericzhou felix]# web界面对于那些喜欢使用命令行的同学,你们可以选择使用web界面来操作 ...

May 22, 2019 · 2 min · jiezi

yii2-的-restful-api-路由实例

yii\rest\UrlRule使用yii\rest\UrlRule来自动映射控制器的 restful 路由,简单快捷,缺点是必须得按规定好的方法名去写业务。 映射的规则如下,当然,你可以修改源码为你的习惯: public $patterns = [ 'PUT,PATCH {id}' => 'update', 'DELETE {id}' => 'delete', 'GET,HEAD {id}' => 'view', 'POST' => 'create', 'GET,HEAD' => 'index', '{id}' => 'options', '' => 'options',];除了被限制了HTTP动词对应的方法名外,其他都很好用,比如pluralize是多么的优雅啊,可以自动解析单词的复数,laravel的话要一个个的去写,反而有些不方便了 'urlManager' => [ 'enablePrettyUrl' => true, 'showScriptName' => false, 'enableStrictParsing' => true, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => [ 'v1/user', 'v1/news', 'routeAlias' => 'v1/box' ], 'pluralize' => true ], ]]自定义路由注意我路由里很刻意的用了复数模式,但很鸡肋,因为一些单词的复数并不是简单的加个 s 就可以了。 ...

May 13, 2019 · 1 min · jiezi

RPC-RESTGraphQL三种API设计方式的简介和比较

RPCRPC=remote procedure call,执行远程服务器上的一个function,举例:服务端定义了三个函数: 客户端发起请求 RPC在一些大公司中依然被使用。RPC的优点有: 设计简洁,便于理解轻量的payload很高的性能表现缺点有: 前后端代码高耦合代码可读性不好,相关代码不容易被定位会导致有大量被定义的函数,难以管理RESTREST = Representational state transfer,直接翻译就是『表现层状态转移』优点: 前后端高度解耦便于理解,即使没有看文档,也能大概知道接口是用来做什么的;接口的功能有单一性,便于扩展和复用;利用了HTTP原本的特性缺点: 有时payload会变的特别大同一个页面可能要调用很多个API,来获取不同的东西,在网络差的情况下会降低体验举例: GraphQLGraphQL = Graph query language吸取了RPC和REST的一些共同优点;以查询为基本单元,方便获取到想要的数据,举例:接口定义接口调用优点: 低网络速度下表现优异声明式地数据获取根据UI需求获取合适的数据,避免不必要的数据传输缺点: 定义起来相对复杂缓存问题,需要一个更加健全的机制中来确保字段级别的缓存版本持续更新中,还不太成熟综合对比与总结API设计也不会有银弹。设计API时,决定使用哪种形式,得先考虑所设计的API将会被谁使用: 如果是关注于对象和资源的项目,需要对接各种不同的端和使用者,需要便于使用和阅读文档,那么适合使用REST如果是面向行为动作,或者内部的一些微服务,对响应要求高,那么可以考虑RPC如果是需要给UI提供数据,或者需要对弱网络环境下优化而减少请求,那么可以考虑GraphQL 参考来源https://www.youtube.com/watch...

April 26, 2019 · 1 min · jiezi

Jenkins API 使用

Jenkins 是一款流行的开源持续集成工具,可以用来做一些软件开发的自动化工作,如打包,测试,自动部署等。 Jenkins 中有 view 和 job 的概念, view 相当于组, job 则是具体的任务。view 下面可以创建 job ,但 job 可以不在任何 view 下。 这里主要介绍 Jenkins 提供的 HTTP API ,至于如何使用 Jenkins 请参看 Jenkins User Documentation。 API鉴权Jenkins 使用 Baisc Auth 的权限验证方式,需要传入 username 和 api token 。其中 api token 需要在用户的设置界面去创建。 但在 Job 的远程触发中,可以设置用于远程触发的 token (在 Job 的配置页面设置),这样在触发 Job 时就不需要传入 Basic Auth 了。远程触发的 token 使用 urlencode 的方式放在请求的 body 中,其原始数据为: token=<Token Value> 下面给出两种方式触发 Job 的例子: ...

April 21, 2019 · 3 min · jiezi

KOA2 Restful方式路由初探

前言最近考虑将服务器资源整合一下,作为多端调用的API看到Restful标准和ORM眼前一亮,但是找了不少版本路由写的都比较麻烦,于是自己折腾了半天API库结构考虑到全部对象置于顶层将会造成对象名越来长,同时不便于维护,故采取部分的分层结构如workflow模块内的prototypes,instances等等,分层的深度定义为层级可访问的对象集合(collection)的属性满足Restful设计 – workflow(category) – prototypes(collection) – [method] … – [method] … – instances(collection) – users(collection) –[method] List #get :object/ –[method] Instance #get :object/:id – … – …RESTFUL API 接口将Restful API接口进行标准化命名.get(’/’, ctx=>{ctx.error(‘路径匹配失败’)}) .get(’/:object’, RestfulAPIMethods.List).get(’/:object/:id’, RestfulAPIMethods.Get).post(’/:object’, RestfulAPIMethods.Post).put(’/:object/:id’, RestfulAPIMethods.Replace).patch(’/:object/:id’, RestfulAPIMethods.Patch).delete(’/:object/:id’, RestfulAPIMethods.Delete).get(’/:object/:id/:related’, RestfulAPIMethods.Related).post(’/:object/:id/:related’, RestfulAPIMethods.AddRelated).delete(’/:object/:id/:related/:relatedId’, RestfulAPIMethods.DelRelated)API对象这个文件是来自微信小程序demo,觉得很方便就拿来用了,放于需要引用的根目录,引用后直接获得文件目录结构API对象const _ = require(’lodash’)const fs = require(‘fs’)const path = require(‘path’)/** * 映射 d 文件夹下的文件为模块 */const mapDir = d => { const tree = {} // 获得当前文件夹下的所有的文件夹和文件 const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory()) // 映射文件夹 dirs.forEach(dir => { tree[dir] = mapDir(path.join(d, dir)) }) // 映射文件 files.forEach(file => { if (path.extname(file) === ‘.js’) { tree[path.basename(file, ‘.js’)] = require(path.join(d, file)) tree[path.basename(file, ‘.js’)].isCollection = true } }) return tree}// 默认导出当前文件夹下的映射module.exports = mapDir(path.join(__dirname))koa-router分层路由的实现创建多层路由及其传递关系执行顺序为 1 – 路径匹配 – 匹配到‘/’结束 – 匹配到对应的RestfulAPI执行并结束 – 继续 2 – 传递中间件 Nest 3 – 下一级路由 4 – 循环 to 1const DefinedRouterDepth = 2let routers = []for (let i = 0; i < DefinedRouterDepth; i++) { let route = require(‘koa-router’)() if (i == DefinedRouterDepth - 1) { // 嵌套路由中间件 route.use(async (ctx, next) => { // 根据版本号选择库 let apiVersion = ctx.headers[‘api-version’] ctx.debug(------- (API版本 [${apiVersion}]) --=-------) if (!apiVersion) { ctx.error(‘版本号未标记’) return } let APIRoot = null try { APIRoot = require(../restful/${apiVersion}) } catch (e) { ctx.error (‘API不存在,请检查Header中的版本号’) return } ctx.debug(APIRoot) ctx.apiRoot = APIRoot ctx.debug(’———————————————’) // for(let i=0;i<) await next() }) } route .get(’/’, ctx=>{ctx.error(‘路径匹配失败’)}) .get(’/:object’, RestfulAPIMethods.List) .get(’/:object/:id’, RestfulAPIMethods.Get) .post(’/:object’, RestfulAPIMethods.Post) .put(’/:object/:id’, RestfulAPIMethods.Replace) .patch(’/:object/:id’, RestfulAPIMethods.Patch) .delete(’/:object/:id’, RestfulAPIMethods.Delete) .get(’/:object/:id/:related’, RestfulAPIMethods.Related) .post(’/:object/:id/:related’, RestfulAPIMethods.AddRelated) .delete(’/:object/:id/:related/:relatedId’, RestfulAPIMethods.DelRelated) if (i != 0) { route.use(’/:object’, Nest, routers[i - 1].routes()) } routers.push(route)}let = router = routers[routers.length - 1]Nest中间件将ctx.apiObject设置为当前层的API对象const Nest= async (ctx, next) => { let object = ctx.params.object let apiObject = ctx.apiObject || ctx.apiRoot if(!apiObject){ ctx.error(‘API装载异常’) return } if (apiObject[object]) { ctx.debug(ctx.apiObject=&gt;ctx.apiObject[object]) ctx.debug(apiObject[object]) ctx.debug(------------------------------------) ctx.apiObject = apiObject[object] } else { ctx.error(API接口${object}不存在) return } await next()}RestfulAPIMethodslet RestfulAPIMethods = {}let Methods = [‘List’, ‘Get’, ‘Post’, ‘Replace’, ‘Patch’, ‘Delete’, ‘Related’, ‘AddRelated’, ‘DelRelated’]for (let i = 0; i < Methods.length; i++) { let v = Methods[i] RestfulAPIMethods[v] = async function (ctx, next) { let apiObject = ctx.apiObject || ctx.apiRoot if (!apiObject) { ctx.error (‘API装载异常’) return } let object = ctx.params.object if (apiObject[object] && apiObject[object].isCollection) { ctx.debug(--- Restful API [${v}] 调用---) if (typeof apiObject[object][v] == ‘function’) { ctx.state.data = await apiObject[object]v ctx.debug(‘路由结束’) return //ctx.debug(ctx.state.data) } else { ctx.error(对象${object}不存在操作${v}) return } } ctx.debug(--- 当前对象${object}并不是可访问对象 ---) await next() }}需要注意的点1、koa-router的调用顺序2、涉及到async注意next()需要加await ...

March 14, 2019 · 2 min · jiezi

RESTful API 中的 Status code 是否要遵守规范

缘起事情是这样的,我在知乎受到邀请回答一个问题,主要是问 ID 找不到到底要不要用 Status 404 。我回答的还是比较早的,那时候只有一两个回答。我本来以为这是没啥争议的,在一个学术的地方讨论学术问题,当然是要遵守规范了,结果过了几个小时大跌眼镜。自造 code 党竟然支持率第一,还好平时见的也很多的全 200 党没有受到支持,不然真的吐血了。为什么要遵守规范一般那种说特殊情况特殊处理,不要拘泥于规范的人,大多都是自己没搞清楚某些知识,拿这句话当作偷懒的借口。其实一般做项目没那么多特殊情况。为了更好的适应各种库大部分完善的 HTTP 请求库,都会依照 RFC 的规范去设计错误处理的流程,虽然处理方式各有不同,但一定会在文档说明错误处理的部分的。使用 RFC 标准能最大限度的兼容各种 HTTP 客户端。你说现在你用的HTTP客户端不处理 Status Code,但是你没法保证将来不重构,重构的时候还是不处理。一般调用 api 使用 js 或者 python 的概率比较大,我们看看知名的库。在 js 里,最近比较流行的 axios 默认会把 200 系列外的 code 归到异常里。在 python 里,最流行的 http client 是 requests ,它更为详尽的预处理了 status code 。为了开发者更好上手另外在管理团队的方面,我们的原则是尽量的减少一个项目的“规范”,这样才能更容易去遵守。能用标准的地方,一定不要自己定一个更复杂的规则。无论是服务端的维护者还是 API 的消费者是会换人流动的,每个进入项目的人熟悉一大堆无谓的自定义项目规范都要成本。更简单的办法是参考大厂其实给项目定规范,最不靠谱的是自己拍脑袋,稍好一点的是去知乎或论坛问,更好一点的是去 google 搜,最简单的是直接去看大厂的产品或者规范啊。 API 本来就是个公开暴露的东西,还有比这更好找参考的吗?我们来看看:Google 遵守规范Github 遵守规范Microsoft 遵守规范 顺便说一句,微软的 API 规范真的很具有指导意义。Twitter 遵守规范阿里云 遵守规范腾讯云 不遵守规范 全部 200 事实上腾讯的技术比较混乱,每个项目都不一样。但最新要执行的统一规范是全部 返回 200 用返回值中的错误码表明错误。百度云 遵守规范我的建议很多人也许用着很简陋的 Web 框架,导致误以为返回了错误码,就不能返回 Response Body 了。其实你返回 204 外的任何 Status Code,最好都伴随着返回 Body 。在项目规范里,可以规定 Status Code 遵照 RFC 标准,或者选定一个集合出来,把一些不常用的去掉。然后如果不是200系列的代码,必须伴随着这样的一个错误结构:{ “error”: “UserNotFound”, “message”: “该用户没有找到” }这样错误分为了三层结构,第一层是 Status Code,使用者能大概知道是什么问题。第二层Error 是一个 Key 使用约定好的无空格的英文,给使用者做判断用,使用者可以根据 Key 自定义接下来的操作。第三层是 message ,有些 Key 使用者可以决定直接把 Message 显示个终端客户。如果是微服务项目,需要要求每个服务不管用什么语言,都要把错误统一成这个样子。如果开发者告诉你框架不支持,那这一定不是个好框架,改重构了。好的框架不仅能让你自定义错误内容,还能做到所谓的“框架自己出错的返回”也由你自定义。比如路由没有找到之类的。最后我实在不明白为什么一个最扯淡的答案,要自造一个 600 的 status code ,可以得票第一。知乎用户到底有没有一点独立的判断精神啊,只要装的一本正经,再摆出来一点资历,哪怕是胡说八道,大家也纷纷点赞。也许真的不适合在知乎去回答技术问题了。 ...

February 23, 2019 · 1 min · jiezi

RESTful杂记

前言在网上找了许久的关于REST的资料,发现网上大部分都是说的比较片面,虽然有部分说出了本质,但也没有详细提出,所以在这里记录一下。RESTful是什么首先,维基百科是这样说的:表现层状态转换(REST,英文:Representational State Transfer)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种万维网软件架构风格,目的是便于不同软件/程序在网络(例如互联网)中互相传递信息这样的概念有点难以理解,了解一个东西,通常可以先了解他的背景,他是为了解决什么问题而出现的? Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。而下面则是他在论文中提出REST的目的。“本文研究计算机科学两大前沿—-软件和网络—-的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。“这段话比较绕口,总结一下,就是REST是一个为了进一步解耦client和server的架构风格。REST风格首先,根据论文可以得知,REST风格是由约束来定义的Web 架构背后的设计基本原理,能够被描述为由一组应用于架构中元素之上的约束组成的架构风格。当将每个约束添加到进化中的风格时,会产生一些影响。通过检查这些影响,我们就能够识别出 Web 的约束所导致的属性。然后就能够应用额外的约束来形成一种新的架构风格,这种风格能够更好地反映出现代 Web 架构所期待的属性。client-servetclient-server之间的解耦,服务提供者和服务消费者互不影响,也是我们常说的前后端分离。前后端分离的优势是比较显著的,改善了用户接口跨多个平台的可移植性;同时通过简化服务器组件,改善了系统的可伸缩性。无状态这个约束使架构拥有了可见性、可靠性和可伸缩性等三个架构属性。 可见性是指能单独的理解一个请求,可靠性是减轻了从局部故障中恢复的任务量, 可伸缩性是指为不必在多个请求之间保 存状态,从而允许服务器组件迅速释放资源。可缓存优势明显,不赘述。统一接口它强调组件之间要有 一个统一的接口。通过在组件接口上应用通用性的软件工程原则,整体的系统架 构得到了简化,交互的可见性(通过方法名即知道动作)也得到了改善。实现与它们所提供的服务是解耦的,这促进了独立的可进化性。然而,付出的代价是,统一接口降低了效率,因为信息都使用标准化的形 式来转移,而不能使用特定于应用的需求的形式。(只能使用put post delete get patch等)解决方法:为需要的动作增加一个 endpoint,使用 POST 来执行动作,比如 POST /resend 重新发送邮件。分层系统分层系统风格通过限制组件的行为(即,每个组件只 能“看到”与其交互的紧邻层),将架构分解为若干等级的层。通过将组件对系统的知识限 制在单一层内,为整个系统的复杂性设置了边界,并且提高了底层独立性。我们能够使用层来封装遗留的服务,使新的服务免受遗留客户端的影响,通过将不常用的功能转移到一个共享的中间组件中,从而简化组件的实现。中间组件还能够通过支持跨多个网络和处理器的负 载均衡,来改善系统的可伸缩性。也就是说服务器和客户端之间的中间层(代理,网关等)代替服务器对客户端的请求进行回应,而客户端不需要关心与它交互的组件之外的事情。按需加载代码通过下载并执行 applet 形式或脚本形式的代码,REST允许对客户端的功能进行扩展。通过减少必须被预先实现的功能的数目,简化了客户端的开发。允许在部署之后下载功能代 码也改善了系统的可扩展性。然而,这也降低了可见性,因此它只是REST的一个可选的约束。设计原则(GITHUB API)了解了REST是什么东西后,我们才能设计出合适的API,以下是根据GITHUB API来总结的(基本参考自:https://cizixs.com/2016/12/12…)使用https这个和 Restful API 本身没有很大的关系,但是对于增加网站的安全是非常重要的。API地址和版本如果 API 变化比较大,可以把 API 设计为子域名,比如 https://api.github.com/v3响应内容尽量使用JSON,JSON在多种语言中支持,如果需要使用其他的如XML, 应该在请求头部 Accept 中指定以资源为中心资源分为单个文档和集合,尽量使用复数来表示资源,单个资源通过添加id或者name等来表示。一个资源可以有多个不同的 URL。资源可以嵌套,通过类似目录路径的方式来表示,以体现它们之间的关系。/users/:username/repos/users/:org/repos/repos/:owner/:repo/repos/:owner/:repo/tags/repos/:owner/:repo/branches/:branch使用正确的METHOD这个比较容易理解,即get(获取),post(创建),put(替换),patch(局部更新),delete(删除),head(获取某个资源的头部信息。比如只想了解某个文件的大小,某个资源的修改日期等)对于不符合CURD的情况,可以采用参数协助如分页page=2&per_page=100:指定第几页,以及每页的记录数,或者增加一个endpoint,如上面说的重发邮件,或者将动作转换为资源(Github:比如“喜欢”一个 gist,就增加一个 /gists/:id/star 子资源,然后对其进行操作:“喜欢”使用 PUT /gists/:id/star,“取消喜欢”使用 DELETE /gists/:id/star)状态码https://developer.mozilla.org…2XX:请求正常处理并返回3XX:重定向,请求的资源位置发生变化4XX:客户端发送的请求有错误5XX:服务器端错误错误处理返回错误时,在响应内容里加上具体的错误信息。Hypermedia API当服务端修改API时,客户端不需要知道和修改。 验证和授权, OAUTH2等限流, 参考githubhttps://developer.github.com/… 对用户的请求限流之后,要有方法告诉用户它的请求使用情况,Github API 使用的三个相关的头部:X-RateLimit-Limit: 用户每个小时允许发送请求的最大值X-RateLimit-Remaining:当前时间窗口剩下的可用请求数目X-RateLimit-Rest: 时间窗口重置的时候,到这个时间点可用的请求数量就会变成 X-RateLimit-Limit 的值编写清晰的文档REST与http的关系?个人理解是REST是一种架构风格,而http则是这种架构实现下的一种协议。比较(以操作为中心)以操作为中心可见性低,即不够清晰。在除了CURD的接口外,以操作为中心调用效率高,不需要hack。以操作为中心没有HyperMidea Api,修改api效率低,需要客户端服务端同时修改。以操作为中心上手难度系数大。以资源为中心,简单数据操作,无事务处理,开发和调用简单, 以操作为中心,清晰的规范标准定义,能够处理较为复杂的面向活动的服务在通常的软件开发过程中,我们常常需要分析达成某个目标所需要使用的业务逻辑,并为业务逻辑的执行提供一系列运行接口。在一些Web服务中,这些接口常常表达了某个动作,如将商品放入购物车,提交订单等。这一系列动作组合在一起就可以组成完成目标所需要执行的业务逻辑。在需要调用这些接口的时候,软件开发人员需要向这些接口所在的URL发送一个请求,从而驱使服务执行该动作

February 12, 2019 · 1 min · jiezi

Restful API 设计(二)

2015年冬天,我写下第一篇也是目前唯一一篇关于 Restful API 设计的文章。时间过的飞快,转眼三年前过去了。这三年间经历过的项目中,后台逐渐微服务化,restful 也成为大家耳熟能详的设计方案。这里记下些自己的经验和教训,以供对照。Status code基本的 code 原则很简单,2xx 表示成功,4xx 表示客户端错误,5xx 表示服务端错误。那如何分辨是客户端还是服务端错误呢?我总结了以下几种常见的客户端错误,以及对应的错误码。401 - 未授权的访问比如访问资源需要 token 鉴权,如果不携带 token 或者 token 已过期,则返回 401.403 - forbidden,禁止访问。比如某些资源只允许管理员访问,非管理员则返回 403。404 - not found,不存在。总之,凡是客户的锅,都返回 4xx 。如果恰好不在上面所列的三种情况中,则用 400 代替。服务端自身错误则包含两类情况:io 错误,比如读写文件,访问数据库自身逻辑错误,比如内存泄漏。第一种错误是不可避免的,属于不可控的外部环境问题。第二种错误虽然可以通过 review 代码加上各种测试来预防,但最好有个兜底的错误处理以免程序挂掉。我司对于服务端错误统一返回 500(internal server error),因为考虑到服务端错误对于客户来讲毫无建设意义,毕竟客户绝对没有办法帮助我们解决错误。即使对于工程师来说,日志也比 code 更有表现力。相对而言,客户端错误则尽量设计的详细因为大部分情况下客户端要据此来引导用户回到正常的业务中来。比如,如果返回 401,则引导用户登陆或者注册。如果业务比较复杂,还要考虑扩展 reponse 来指明更加具体的错误。如:400 bad request{ “code”: 123, “message”: “Name is required”}List APIGET /orders200 OK{ “offset”: 0, “limit”: 20, “count”: 100, “elements”: […]}对于这个 List API,如果资源不存在,返回应该是什么。受 404 概念的普及影响,很多人会选择返回404 NotFound难道说,如果不存在 orders(订单) 就是错误吗?比如我从来没有在淘宝下过单,那订单列表也就应该显示客户端错误吗?这显然是不对的。实际上,404 是指所请求的资源不存在。而对于 orders 来说,它是一个集合概念。不管下没下过单,这个集合总归是存在的。按照这个理论,正确的返回应该是:200 OK{ “offset”: 0, “limit”: 20, “count”: 100, “elements”: [] // 空数组}所以对于 List API 来说,没有 404。Parent resourcerestful API 的路径可以表现资源的从属关系。比如,用户可以有多个地址。/users/{user_id}/addresses/那么,对于一个并不存在的用户而言,访问上述 API,应该返回什么?用户不存在,他的地址也必然不存在,那似乎是个简单的客户端错误。但我们确实有必要参考 Parent resource 的状态吗?这从理论上讲似乎毫无破绽,但实际操作及其困难。假如 Parent resource 的状态为 s1, Child resource 的状态为 s2,如果必须参考 s1 才能定义 s2,则 Child resource 的状态为 s1 s2。这还是简单的层次,如果 Parent 之上还有 Parent,则最终 Child 的状态会变成 s0 s1 * s2。如果随着业务的升级,每个节点的状态推算都要这样越来越复杂,那结果必然是整个系统的崩塌。所以,目前比较推崇的做法是,仅仅考虑目标资源或者资源集合的状态。即,addresses,不管它从属于谁。 ...

January 23, 2019 · 1 min · jiezi

Yii2.0 RESTful API 之版本控制

Yii2.0 RESTful API 之版本控制之前我写过两篇关于 Yii2.0 RESTful API 如何搭建,以及 认证 等处理,但是没有涉及到版本管理,今天就来谈谈版本管理如何实现。索性就从头开始一步一步搭建吧,但是关于一些概念以及使用本篇就不一一解释了,可以参考 第一篇 Yii2.0 RESTful API 基础配置教程 进行配置安装Yii2.0通过 Composer 安装这是安装Yii2.0的首选方法。如果你还没有安装 Composer,你可以按照这里的说明进行安装。安装完 Composer,运行下面的命令来安装 Composer Asset 插件:composer global require “fxp/composer-asset-plugin:^1.2.0"安装高级的应用程序模板,运行下面的命令:composer create-project yiisoft/yii2-app-advanced yii-api 2.0.14拷贝backend目录,命名为api打开api\config\main.php 修改id,controllerNamespace:return [ ‘id’ => ‘app-api’, ‘basePath’ => dirname(DIR), ‘controllerNamespace’ => ‘api\controllers’,]初始化高级模板在初始化之前不妨先看下这篇文章cd advancedphp init打开common\config\main.php开启url路由美化规则’urlManager’ => [ ’enablePrettyUrl’ => true, ‘showScriptName’ => false, ‘rules’ => [ ],],打开common\config\bootstrap.php添加以下别名Yii::setAlias(’@api’, dirname(dirname(DIR)) . ‘/api’);ok,以上工作准备完毕,接下来进入正题,关于版本更多介绍可以参考 权威指南 ,这里不过多解释(PS:主要我也不会……)我的理解:Yii2 的版本你可以理解为不同的模块,每一个版本就是一个新的模块,比如常见的v1,v2等。模块的搭建关于如何生成模块,我们可以使用GII来进行生成.配置 GII打开 api/config/main-local.php 文件 修改如下:if (!YII_ENV_TEST) { // configuration adjustments for ‘dev’ environment $config[‘bootstrap’][] = ‘debug’; $config[‘modules’][‘debug’] = [ ‘class’ => ‘yii\debug\Module’, ]; $config[‘bootstrap’][] = ‘gii’; $config[‘modules’][‘gii’] = [ ‘class’ => ‘yii\gii\Module’, ‘allowedIPs’ => [‘127.0.0.1’, ‘’] ];}我这里因为使用的是 Homestead ,默认是不允许访问 GII 的,所以得加上 ‘allowedIPs’ => [‘127.0.0.1’, ‘’] ,否则会出现 Forbidden (#403), 你可以根据自己的需要来进行配置,或者不配置生成Modules浏览器中输入 http://your host/gii ,可以看到 Module Generator ,点击 StartModules Class 中输入:api\modules\v1\ModuleModule ID 中输入v1,(一般会自动输入)点击 Preview最后点击 Generate 进行生成配置模块打开 api/config/main.php 文件,修改 modules’modules’ => [ ‘v1’=>[ ‘class’=>‘api\modules\v1\Module’, ],],接着修改 urlManager’urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘v1/default’, ’extraPatterns’=>[ ‘GET index’=>‘index’, ], ], ],],基于以上,Yii2.0 RESTFul API 就实现了版本管理,我们可以通过如下地址进行访问:http://localhost/v1/defaults多说一点,我上方的地址是已经映射到api/web目录,请根据自己的实际情况进行配置打开刚生成的 modules 文件目录,可以看到里面存在一个 v1 的目录,可以看到该目录还有一个controllers,以及一个 views 目录,我们刚才访问的 defaults 其实就是这两个文件,和传统的web项目一样控制器渲染视图好了,你可能知道了,我们以后的控制器代码就放到 modules/v1/controllers 里了刚才仅仅是默认GII为我们生成的代码,因为我们是API,所以 views 目录,我们一般情况下用不到。新建一个 rest 的控制器在 modules\v1\controllers 下新建 UserController<?phpnamespace api\modules\v1\controllers;use yii\rest\Controller;/** * User controller for the v1 module /class UserController extends Controller{ /* * @return string */ public function actionIndex() { return ’this is v1/user’; }}修改 api/config/main.php 中的urlManager’urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘v1/default’, ’extraPatterns’=>[ ‘GET index’=>‘index’, ], ], [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘v1/user’, ’extraPatterns’=>[ ‘GET index’=>‘index’, ], ], ],],试着访问下http://localhost/v1/users/indexok,以上就是 Yii2.0 版本管理的实现方式格式化响应修改 api/config/main.php 在components 数组中添加 response’response’ => [ ‘class’ => ‘yii\web\Response’, ‘on beforeSend’ => function ($event) { $response = $event->sender; $response->data = [ ‘success’ => $response->isSuccessful, ‘code’ => $response->getStatusCode(), ‘message’ => $response->statusText, ‘data’ => $response->data, ]; $response->statusCode = 200; },],至此关于 Yii2.0 RESTFul API 我一共完成了 3 篇文章,分别为:Yii2.0 RESTful API 基础配置教程Yii2.0 RESTful API 认证教程Yii2.0 RESTful API 之版本控制写得实在不怎么样,您如果看了有收获,不妨留言给个评论,或者您觉得写得有问题,或者不明白,也可以留言,我们可以一块探讨研究。 ...

January 9, 2019 · 2 min · jiezi

Yii2.0 RESTful API 之速率限制

Yii2.0 RESTful API 之速率限制什么是速率限制?权威指南翻译过来为限流,为防止滥用,你应该考虑对您的 API 限流。 例如,您可以限制每个用户 10 分钟内最多调用 API 100 次。 如果在规定的时间内接收了一个用户大量的请求,将返回响应状态代码 429 (这意味着过多的请求)。要启用速率限制,首先需要实现认证类,而关于认证的章节我在 Yii2.0 RESTful API 认证教程 进行了详细的阐述,本篇就不过多介绍,再次基础上进行操作启用速率限制翻阅权威指南,我们可以看到要启用速率限制首先 认证类 需要继承 yiifiltersRateLimitInterface生成两个关键字段php yii migrate/create add_allowance_and_allowance_updated_at_to_user修改 刚才的迁移文件/** * {@inheritdoc} /public function safeUp(){ $this->addColumn(‘user’, ‘allowance’, $this->integer()); $this->addColumn(‘user’, ‘allowance_updated_at’, $this->integer());}/* * {@inheritdoc} */public function safeDown(){ $this->dropColumn(‘user’, ‘allowance’); $this->dropColumn(‘user’, ‘allowance_updated_at’);}执行迁移php yii migrate编写认证类,并继承 RateLimitInterfacenamespace api\models;use Yii;use yii\base\NotSupportedException;use yii\behaviors\TimestampBehavior;use yii\db\ActiveRecord;use yii\filters\RateLimitInterface;use yii\web\IdentityInterface;class User extends ActiveRecord implements IdentityInterface,RateLimitInterface{ . . .}实现 RateLimitInterface 所需要的方法public function getRateLimit($request, $action){ return [1, 1]; // $rateLimit requests per second}public function loadAllowance($request, $action){ return [$this->allowance, $this->allowance_updated_at];}public function saveAllowance($request, $action, $allowance, $timestamp){ $this->allowance = $allowance; $this->allowance_updated_at = $timestamp; $this->save();}控制器中实现调用use yii\filters\auth\CompositeAuth;use yii\filters\auth\HttpBearerAuth;use yii\filters\auth\QueryParamAuth;use yii\filters\RateLimiter;public function behaviors(){ $behaviors = parent::behaviors(); $behaviors[‘rateLimiter’] = [ ‘class’ => RateLimiter::className(), ’enableRateLimitHeaders’ => true, ]; $behaviors[‘authenticator’] = [ ‘class’ => CompositeAuth::className(), ‘authMethods’ => [ //Http::className(), HttpBearerAuth::className(), QueryParamAuth::className(), ], ]; //$behaviors[‘rateLimiter’][’enableRateLimitHeaders’] = true; return $behaviors;}ok,请求下你的 action,多次请求如果出现 429,那么表示速率限制启用成功以上就是关于 Yii2.0 速率限制的使用,速率限制需要和认证配合着使用,关于认证的,查阅Yii2.0 RESTful API 认证教程 ,这篇文章,推荐您,先看完认证,先做完认证的功能,然后在启用速率限制 关于 Yii2.0 RESTFul API到此我觉得就结束了,核心功能就是这些,剩下的就是具体的实战了,多练、多敲,一共四篇文章,分别为:Yii2.0 RESTful API 基础配置教程Yii2.0 RESTful API 认证教程Yii2.0 RESTful API 之版本控制Yii2.0 RESTful API 之速率限制 ...

January 9, 2019 · 1 min · jiezi

Yii2.0 RESTful API 认证教程【令牌验证】

最近在做RESTful API认证功能,记录整个过程,方便以后查看。本文参照了 https://segmentfault.com/a/1190000016368603部分内容,感谢该作者的分享,以下内容根据我的项目实际情况进行了调整。认证介绍和Web应用不同,RESTful APIs 通常是无状态的, 也就意味着不应使用 sessions 或 cookies, 因此每个请求应附带某种授权凭证,因为用户授权状态可能没通过 sessions 或 cookies 维护, 常用的做法是每个请求都发送一个秘密的 access token 来认证用户, 由于 access token 可以唯一识别和认证用户,API 请求应通过 HTTPS 来防止man-in-the-middle (MitM) 中间人攻击.认证方式HTTP 基本认证 :access token 当作用户名发送,应用在access token可安全存在API使用端的场景, 例如,API使用端是运行在一台服务器上的程序。请求参数: access token 当作API URL请求参数发送,例如 https://example.com/users?acc…, 由于大多数服务器都会保存请求参数到日志, 这种方式应主要用于JSONP 请求,因为它不能使用HTTP头来发送 access tokenOAuth 2 : 使用者从认证服务器上获取基于 OAuth2 协议的 access token, 然后通过 HTTP Bearer Tokens 发送到 API 服务器。上方进行简单介绍,内容来自 Yii Framework 2.0 权威指南实现步骤继续上一篇 的内容(这里暂时使用默认User数据表,正式环境请分离不同的数据表来进行认证)需要添加的数据内容继上篇的 User 数据表,我们还需要增加一 个 access_token 和 expire_at 的字段,进入项目根目录打开控制台输入以下命令:./yii migrate/create add_column_access_token_to_user./yii migrate/create add_column_expire_at_to_user打开 你的项目目录 /console/migrations/m181224_075747_add_column_access_token_user.php 修改如下内容: public function up() { $ret = $this->db->createCommand(“SELECT * FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ‘user’ AND column_name = ‘access_token’”)->queryOne();//判断user表是否有’access_token’这个字段 if (empty($ret)) { $this->addColumn(‘user’, ‘access_token’, $this->string(255)->defaultValue(NULL)->comment(‘令牌’)); } } public function down() { $this->dropColumn(‘user’, ‘access_token’); return true; }打开 你的项目目录 /console/migrations/m181224_092333_add_column_expire_at_user.php 修改如下内容: public function up() { $ret = $this->db->createCommand(“SELECT * FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ‘user’ AND column_name = ’expire_at’”)->queryOne(); if (empty($ret)) { $this->addColumn(‘user’, ’expire_at’, $this->integer(11)->defaultValue(NULL)->comment(‘令牌过期时间’)); } } public function down() { $this->dropColumn(‘user’, ’expire_at’); return true; }执行迁移命令./yii migrate配置打开 api\config\main.php配置 user 应用组件:‘user’ => [ ‘identityClass’ => ‘api\models\User’, ’enableAutoLogin’ => true, ’enableSession’=>false, //‘identityCookie’ => [’name’ => ‘_identity-api’, ‘httpOnly’ => true], ],将 session 组件注释掉,或删掉// ‘session’ => [// // this is the name of the session cookie used for login on the backend// ’name’ => ‘advanced-api’,// ],编写 api\models\User.php 实现认证类,继承 IdentityInterface将 common\models\User 类拷贝到 api\models\ 目录下,修改命名空间为 api\models<?phpnamespace api\models;use Yii;use yii\base\NotSupportedException;use yii\behaviors\TimestampBehavior;use yii\db\ActiveRecord;use yii\web\IdentityInterface;…class User extends ActiveRecord implements IdentityInterface{ … …}将 commonmodelsLoginForm.php 类拷贝到 apimodels* 目录下,修改命名空间,并重写 login* 方法:<?phpnamespace api\models;use Yii;use yii\base\Model;……const EXPIRE_TIME = 604800;//令牌过期时间,7天有效public function login(){ if ($this->validate()) { //return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); if ($this->getUser()) { $access_token = $this->_user->generateAccessToken(); $this->_user->expire_at = time() + static::EXPIRE_TIME; $this->_user->save(); Yii::$app->user->login($this->_user, static::EXPIRE_TIME); return $access_token; } } return false;}上方代码给 User 模型添加了一个 generateAccessToken() 方法,因此我们到 api\models\User.php 中添加此方法namespace api\models;use Yii;use yii\base\NotSupportedException;use yii\behaviors\TimestampBehavior;use yii\db\ActiveRecord;use yii\web\IdentityInterface;use yii\web\UnauthorizedHttpException;……class User extends ActiveRecord implements IdentityInterface{ … … /** * 生成accessToken字符串 * @return string * @throws \yii\base\Exception / public function generateAccessToken() { $this->access_token=Yii::$app->security->generateRandomString(); return $this->access_token; }}接下来在api\controllers\新加一个控制器 命名为 UserController 并继承 yii\rest\ActiveController,编写登录 Login 方法,具体代码如下:namespace api\controllers;use api\models\LoginForm;use yii\rest\ActiveController;use yii;class UserController extends ActiveController{ public $modelClass = ‘api\models\User’; public function actions() { $action= parent::actions(); // TODO: Change the autogenerated stub unset($action[‘index’]); unset($action[‘create’]); unset($action[‘update’]); unset($action[‘delete’]); } public function actionIndex() { //你的代码 } /* * 登陆 * @return array * @throws \yii\base\Exception * @throws \yii\base\InvalidConfigException / public function actionLogin() { $model = new LoginForm(); if ($model->load(Yii::$app->getRequest()->getBodyParams(), ‘’) && $model->login()) { return [ ‘access_token’ => $model->login(), ]; } else { return $model->getFirstErrors(); } }}最后新增一条 URL 规则打开 api\config\main.php 修改 components 属性,添加下列代码:‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, ’extraPatterns’=>[ ‘POST login’=>’login’, ], ], ],]使用一个调试工具来进行测试 http://youdomain/users/login 记住是POST 请求发送,假如用POSTMAN有问题的话指定一下 Content-Type:application/x-www-form-urlencoded 。ok,不出意外的话,相信你已经可以收到一个access_token 了,接下来就是如何使用这个token,如何维持认证状态,达到不携带这个token将无法访问,返回 401维持认证状态实现认证步骤:在你的 REST 控制器类中配置 authenticator 行为来指定使用哪种认证方式在你的 user identity class 类中实现 yiiwebIdentityInterface::findIdentityByAccessToken() 方法.具体实现方式如下:打开之前的 User 控制器( api\controllers\UserController.php ),增加以下内容:use yii\helpers\ArrayHelper;use yii\filters\auth\QueryParamAuth;…//此处省略一些代码了 public function behaviors() { return ArrayHelper::merge(parent::behaviors(), [ ‘authenticatior’ => [ ‘class’ => QueryParamAuth::className(), //实现access token认证 ’except’ => [’login’], //无需验证access token的方法,注意区分$noAclLogin ] ]); }… 实现 findIdentityByAccessToken() 方法:打开 api\models\User.php 重写 findIdentityByAccessToken() 方法…use yii\web\UnauthorizedHttpException;…class User extends ActiveRecord implements IdentityInterface{ … … /* * {@inheritdoc} */ public static function findIdentityByAccessToken($token, $type = null) {// throw new NotSupportedException(’“findIdentityByAccessToken” is not implemented.’); $user = static::find()->where([‘access_token’ => $token, ‘status’ => self::STATUS_ACTIVE])->one(); if (!$user) { return false; } if ($user->expire_at < time()) { throw new UnauthorizedHttpException(’the access - token expired ‘, -1); } else { return $user; } } …}打开 api\controllers\UserController.php ,增加 Test方法,用于测试令牌验证 public function actionTest() { return [‘status’=>‘success’]; }修改 api\config\main.php ‘urlManager’ => [ ’enablePrettyUrl’ => true, ’enableStrictParsing’ => true, ‘showScriptName’ => false, ‘rules’ => [ [‘class’ => ‘yii\rest\UrlRule’, ‘controller’ => ‘user’, //‘pluralize’ => false, //设置为false 就可以去掉复数形式了 ’extraPatterns’=>[ ‘GET test’=>’test’, ‘POST login’=>’login’, ], ], ], ]接下来访问一下你的域名 http://youdomain/users/test,不携带任何参数是不是返回 401了?ok,这里介绍两种访问方式,一种是URL访问,另一种是通过header 来进行携带http://youdomain/users/test?a…传递 header 头信息Authorization:Bearer YYdpiZna0hJGhjsfqwxUeHEgLDfHEjB- 注意 Bearer 和你的token中间是有 一个空格的,很多同学在这个上面碰了很多次 以上就是基于YII2.0 RESTful 认证的内容。本文参照了 https://segmentfault.com/a/1190000016368603部分内容,感谢该作者的分享,以上内容根据我的项目实际情况进行了调整。 ...

December 25, 2018 · 3 min · jiezi

13 个设计 REST API 的最佳实践

原文 RESTful API Design: 13 Best Practices to Make Your Users Happy写在前面之所以翻译这篇文章,是因为自从成为一名前端码农之后,调接口这件事情就成为了家常便饭,并且,还伴随着无数的争论与无奈。编写友好的 restful api 不论对于你的同事,还是将来作为第三方服务调用接口的用户来说,都显得至关重要。关于 restful api 本身以及设计原则,我陆陆续续也看过很多的文章和书籍,在读过原文后,感觉文中指出的 13 点最佳实践还是比较全面的且具有参考意义的,因此翻译出来分享给大家。如有错误,还望指正。由于我一般倾向于意译,关于原文中的开头语或者一些与之无关的内容,我就省略掉了,毕竟时间是金钱,英语好并且能科学上网的朋友我建议还是看原文,以免造成理解上的误差。1. 了解应用于 REST 之上的 HTTP 知识如果你想要构建设计优良的 REST API,了解一些关于 HTTP 协议的基础知识是很有帮助的,毕竟磨刀不误砍材工。在 MDN 上有很多质量不错的文档介绍 HTTP。但是,就 REST API 设计本身而言,所涉及到的 HTTP 知识要点大概包含以下几条:HTTP 中包含动词(或方法): GET、POST、PUT、PATCH 还有 DELETE 是最常用的。REST 是面向资源的,一个资源被一个 URI 所标识,比如 /articles/。端点(endpoint),一般指动词与 URI 的组合,比如 GET: /articles/。一个端点可以被解释为对某种资源进行的某个动作。比如, POST: /articles 可能代表“创建一个新的 article”。在业务领域,我们常常可以将动词和 CRUD(增删查改)关联起来:GET 代表查,POST代表增,PUT 和 PATCH 代表改(注: PUT 通常代表整体更新,而 PATCH 代表局部更新),而 DELETE 代表删。当然了,你可以将 HTTP 协议中所提供的任何东西应用于 REST API 的设计之中,但以上这些是比较基础的,因此时刻将它们记在脑海中是很有必要的。2. 不要返回纯文本虽然返回 JSON 数据格式的数据不是 REST 架构规范强制限定的,但大多 REST API 都遵循这条准则。但是,仅仅返回 JSON 数据格式的数据还是不够的,你还需要指定返回 body 的头部,比如 Content-Type,它的值必须指定为 application/json。这一点对于程序化客户端尤为重要(比如通过 python 的 requests 模块来与 api 进行交互)—— 这些程序是否对返回数据进行正确解码取决于这个头部。注:通常而言,对于浏览器来说,这似乎不是问题,因为浏览器一般都自带内容嗅探机制,但为了保持一致性,还是在响应中设置这个头部比较妥当。3. 避免在 URI 中使用动词如果你理解了第 1 条最佳实践所传达的意思,那么你现在就会明白不要将动词放入 REST API 的 URI 中。这是因为 HTTP 的动词已经足以描述执行于资源的业务逻辑操作了。举个例子,当你想要提供一个针对某个 article 提供 banner 图片并返回的接口时,可能会实现如下格式的接口:GET: /articles/:slug/generateBanner/这里 GET 已经说明了这个接口是在做读的操作,因此,可以简化为:GET: /articles/:slug/banner/类似的,如果这个端口是要创建一个 article:// 不要这么做POST: /articles/createNewArticle/// 这才是最佳实践POST: /articles/尝试用 HTTP 的动词来描述所涉及的业务逻辑操作。4. 使用复数的名词来描述资源一些时候,使用资源的复数形式还是单数形式确实存在一定的困扰,比如使用 /article/:id/ 更好还是使用 /articles/:id/ 更好呢?这里我推荐使用后者。为什么呢?因为复数形式可以满足所有类型端点的需求。单数形式的 GET /article/2/ 看起来还是不错的,但是如果是 GET /article/ 呢?你能够仅通过字面信息来区分这个接口是返回某个 article 还是多个呢?因此,为了避免有单数命名造成的歧义性,并尽可能的保持一致性,使用复数形式,比如:GET: /articles/2/POST: /articles/…5. 在响应中返回错误详情当 API 服务器处理错误时,如果能够在返回的 JSON body 中包含错误信息,对于接口调用者来说,会一定程度上帮助他们完成调试。比如对于常见的提交表单,当遇到如下错误信息时:{ “error”: “Invalid payoad.”, “detail”: { “surname”: “This field is required.” }}接口调用者很快就是明白发生错误的原因。6. 小心 status code这一点可能是最重要、最重要、最重要的一点,可能也是这篇文章中,唯一你需要记住的那一点。你可能知道,HTTP 中你可以返回带有 200 状态码的错误响应,但这是十分糟糕的。不要这么做,你应当返回与返回错误类型相一致的具有一定含义的状态码。聪明的读者可能会说,我按照第 5 点最佳实践来提供足够详细的信息,难道不行吗?当然可以,不过让我讲一个故事:我曾经使用过一个 API,对于它返回的所有响应的状态码均是 200 OK,同时通过响应数据中的 status 字段来表示当前的请求是否成功,比如:{ “status”: “success”, “data”: {}}所以,虽然状态码是 200 OK,但我却不能绝对确定请求是否成功,事实上,当错误发生时,这个 API 会按如下代码片段返回响应:HTTP/1.1 200 OKContent-Type: text/html{ “status”: “failure”, “data”: { “error”: “Expected at least two items in list.” }}头部还是 text/html,因为它同时返回了一些 HTML 片段。正因为这样,我不得不在检查响应状态码正确的同时,还需校验这个具有特殊含义的 status 字段的值,才可以放心的处理响应返回的 data。这种设计的一个真正坏处在于,它打破了接口与调用者之间的“信任”,因为你可能会担心这个接口对你撒谎(注:言外之意就是,由于特设的字段可能会改变,因此增加了不可靠性)。所以,使用正确的状态码,同时仅在响应的 body 中返回错误信息,并设置正确的头部,比如:HTTP/1.1 400 Bad RequestContent-Type: application/json{ “error”: “Expected at least two items in list."}7. 保持 status code 的一致性当你掌握了正确使用状态码之后,就应该努力使它们具有一致性。比如,如果一个 POST 类型的端点返回 201 Created,那么所有的 POST 端点都应返回同样的状态码。这样做的好处在于,调用者无需在意端点返回的状态码取决于某种特殊条件,也就形成了一致性。如果有特殊情况,请在文档中显著地说明它们。下面是我推荐的与动词相对应的状态码:GET: 200 OKPOST: 201 CreatedPUT: 200 OKPATCH: 200 OKDELETE: 204 No Contenthttps://blog.florimondmanca.c…8. 不要嵌套资源使用 REST API 获取资源数据,通常情况下会直接获取多个或者单个,但当我们需要获取相关联的资源时,该怎么做呢?比如说,我们期望获取作者为某个 author 的 article 列表 —— 假设 authro 的 id=12。这里提供两种方案:第一种方案通过在 URI 中,将嵌套的资源放在所关联的资源后边来进行描述,比如:GET: /authors/12/articles/一些人推荐这种方案的理由是,这种形式的 URI 一定程度上描述了 author 与 article 之间的一对多关系。但与此同时,结合第 4 点最佳实践,我们就不太能够分清当前端点返回的数据到底是 author 类型还是 article 类型。这里有一篇文章,详细阐述了扁平化形式优于嵌套形式,因此一定有更好的方法,这就是下面的第二种方案:GET: /articles/?author_id=12直接将筛选 article 的逻辑抽离为 querystring 即可,这样的 URI 相比之前,更加清晰地描述了“获取所有 author(id=12) 的 article”的意思。9. 优雅地处理尾部斜杠一个好的 URI 中是否应当包含尾部斜杠,并不具有探讨价值,选择一种更倾向的风格并保持一致性即可,同时当客户端误用尾部斜杠时,提供重定向响应。我再来讲我自己的一个故事。某天,我在将某个 API 端点集成到项目中,但是我总是收到 500 Internal Error 的错误,我调用的端点差不多看起来这样:POST: /entities调试一段时间之后,我几乎崩溃了,因为我根本不知道我哪里做错了,直到我发现服务器之所以报 500 的错误,是因为我粗心丢掉了尾部斜杠(注:这种经历人人都会遇到,我在 SF 上遇过无数次类似的问题),当我把 URI 改成:POST: /entities/之后,一切正常运转。当然,大多数的 web 框架都针对 URL 是否包含尾部斜杠,进行了优雅地处理并提供定制选项,如果可以的话,找到它并开启这项功能。10. 使用 querystring 来完成筛选和分页功能大部分情况下,一个简单的端点没有办法满足负责业务场景。你的用户可能想要获取满足一定条件下的某些数据集合 ,同时为了保证性能,仅仅获取这个集合下的一个子集。换言之,这通常叫作筛选功能和分页功能:筛选:用户可以提供额外的属性来控制返回的数据集合分页:获取数据集合的子集,最简单的分页是基于分页个数的分页,它由 page 和 page_size 来决定那么问题来了,我们如何将这两项功能与 RESTful API 结合在一起呢?答案当然是通过 querystring。对于分页,很显然使用这种方式再合适不过了,比如:GET: /articles/?page=1&page_size=10但对于筛选,你可能会犯第 8 点最佳实践中所指出的问题,比如获取处于 published 状态的 article 列表:GET: /articles/published/除了之前提出的问题外,这里还涉及一个设计上的问题,就是 published 本身不是资源,它仅仅是资源的特征,类似这种特征字段,应该将它们放到 querystring 中:GET: /articles/?published=true&page=2&page_size=20更加优雅、清晰,不是吗?11. 分清 401 和 403当我们遇到 API 中关于安全的错误提示时,很容易混淆这两个不同类型的错误,认证和授权(比如权限相关)—— 老实讲,我自己也经常搞混。这里是我自己总结的备忘录,它阐述了我如何在实际情况下,区分它们:用户是否未提供身份验证凭据?认证是否还有效?这种类型的错误一般是未认证(401 Unauthorized)。用户经过了正常的身份验证,但没有访问资源所需的权限?这种一般是未授权(403 Forbidden)12. 巧用 202 Accepted我发现 202 Accepted 在某些场合是 201 Created 的一个非常便捷的替代方案,这个状态码的含义是:服务器已经接受了你的请求,但是到目前为止还未创建新的资源,但一切仍处于正常状态。我分享两种特别适合使用 202 Accepted 状态码的业务场景:如果资源是经过位于将来一系列处理流程之后才创建的,比如当某项作业完成时如果资源已经存在,但这是理想状态,因此不应该被识别为一个错误时13. 采用 REST API 定制化的框架作为最后一个最佳实践,让我们来探讨这样一个问题:你如何在 API 的实施中,实践最佳实践呢?通常的情况是这样的,你想要快速创建一个 API 以便一些服务可以互相访问彼此。Python 开发者可能马上掏出了 Flask,而 JS 开发者也不甘示弱,祭出了 Express,他们会使用实现一些简单的 routes 来处理 HTTP 请求。但这样做的问题是,通常,web 框架并不是针对构建 REST API 服务而专门存在的,换言之,Flask 和 Express 是两个十分通用的框架,但它们并非特别适合用于构建 REST API 服务。因此,你必须采取额外的步骤来实施 API 中的最佳实践,但大多数情况下,由于懒惰或者时间紧张等因素,意味着你不会投入过多精力在这些方面 —— 然后给你的用户提供了一个古怪的 API 端点。解决方案十分简单:工欲善其事,必先利其器,掌握并使用正确的工作才是最好的方案。在各种语言中,许多专门用于构建 REST API 服务的新框架已经出现了,它们可以帮助你在不牺牲生产力的情况下,轻松地完成工作,同时遵循最佳实践。在 Python 中,我发现的最好的 API 框架之一是 Falcon。它与 Flask 一样简单,非常高效,十分适合构建 REST API 服务。如果你更喜欢 Django 的话,使用 Django REST Framework就足够了,虽然框架不是那么直观(注:按我的理解应该是说不太容易上手,但是我不这么认为),但功能非常强大。在 NodeJS 中,Restify 似乎也是一个不错的选择,尽管我还没有尝试过。我强烈建议你给这些框架一个机会!它们将帮助你构建规范,优雅且设计良好的 REST API 服务。总结我们都应致力于让调用 API 这件事成为一种乐趣。希望本文能使你了解到在构建更好的 REST API 服务的过程中,涉及到的一些建议和技巧。对我而言,应该把这些最佳实践归结为三点,分别是良好的语义,简洁和合理性。关注公众号 全栈101,只谈技术,不谈人生 ...

December 20, 2018 · 2 min · jiezi

???? Apiggs - 非侵入的RestDoc文档生成工具

前言程序员一直以来都有一个烦恼,只想写代码,不想写文档。代码就表达了我的思想和灵魂。Python提出了一个方案,叫docstring,来试图解决这个问题。即编写代码,同时也能写出文档,保持代码和文档的一致。docstring说白了就是一堆代码中的注释。Python的docstring可以通过help函数直接输出一份有格式的文档,本工具的思想与此类似。代码即文档Apiggs是一个非侵入的RestDoc文档生成工具。工具通过分析代码和注释,获取文档信息,生成RestDoc文档。引入插件apiggs-gradle-pluginapiggs-maven-plugin有这样一段代码/** * 欢迎使用Apiggs * @index 1 /@RestControllerpublic class GreetingController { private static final String template = “Hello, %s!”; private final AtomicLong counter = new AtomicLong(); /* * 示例接口 * @param name 名称 * @return */ @RequestMapping("/greeting") public Greeting greeting(@RequestParam(value=“name”, defaultValue=“apiggs”) String name) { return new Greeting(counter.incrementAndGet(), String.format(template, name)); }}运行插件gradle 运行 task:Tasks/documentation/apiggsmaven 运行compile生成文档在编译目录下生成apiggs文件夹,并生成三个文件:.json文件,可直接导入postman.adoc文件,Asciidoc源文件.html文件,源文件渲染结果,效果如下图想了解更多,请查看Wiki

October 30, 2018 · 1 min · jiezi

带入gRPC:让你的服务同时提供 HTTP 接口

带入gRPC:让你的服务同时提供 HTTP 接口原文地址:带入gRPC:让你的服务同时提供 HTTP 接口项目地址:https://github.com/EDDYCJY/go…前言接口需要提供给其他业务组访问,但是 RPC 协议不同无法内调,对方问能否走 HTTP 接口,怎么办?微信(公众号、小程序)等第三方回调接口只支持 HTTP 接口,怎么办我相信你在实际工作中都会遇到如上问题,在 gRPC 中都是有解决方案的,本章节将会进行介绍 ????为什么可以同时提供 HTTP 接口关键一点,gRPC 的协议是基于 HTTP/2 的,因此应用程序能够在单个 TCP 端口上提供 HTTP/1.1 和 gRPC 接口服务(两种不同的流量)怎么同时提供 HTTP 接口检测协议if r.ProtoMajor == 2 && strings.Contains(r.Header.Get(“Content-Type”), “application/grpc”) { server.ServeHTTP(w, r)} else { mux.ServeHTTP(w, r)}流程检测请求协议是否为 HTTP/2判断 Content-Type 是否为 application/grpc(gRPC 的默认标识位)根据协议的不同转发到不同的服务处理gRPCTLS在前面的章节,为了便于展示因此没有简单封装在本节需复用代码,重新封装了,可详见:go-grpc-example目录结构新建 simple_http_client、simple_http_server 目录,目录结构如下:go-grpc-example├── client│ ├── simple_client│ ├── simple_http_client│ └── stream_client├── conf├── pkg│ └── gtls├── proto├── server│ ├── simple_http_server│ ├── simple_server│ └── stream_serverServer在 simple_http_server 目录下新建 server.go,写入文件内容:package mainimport ( “context” “log” “net/http” “strings” “github.com/EDDYCJY/go-grpc-example/pkg/gtls” pb “github.com/EDDYCJY/go-grpc-example/proto” “google.golang.org/grpc”)type SearchService struct{}func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " HTTP Server"}, nil}const PORT = “9003"func main() { certFile := “../../conf/server/server.pem” keyFile := “../../conf/server/server.key” tlsServer := gtls.Server{ CertFile: certFile, KeyFile: keyFile, } c, err := tlsServer.GetTLSCredentials() if err != nil { log.Fatalf(“tlsServer.GetTLSCredentials err: %v”, err) } mux := GetHTTPServeMux() server := grpc.NewServer(grpc.Creds(c)) pb.RegisterSearchServiceServer(server, &SearchService{}) http.ListenAndServeTLS(”:"+PORT, certFile, keyFile, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.ProtoMajor == 2 && strings.Contains(r.Header.Get(“Content-Type”), “application/grpc”) { server.ServeHTTP(w, r) } else { mux.ServeHTTP(w, r) } return }), )}func GetHTTPServeMux() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“eddycjy: go-grpc-example”)) }) return mux}http.NewServeMux:创建一个新的 ServeMux,ServeMux 本质上是一个路由表。它默认实现了 ServeHTTP,因此返回 Handler 后可直接通过 HandleFunc 注册 pattern 和处理逻辑的方法http.ListenAndServeTLS:可简单的理解为提供监听 HTTPS 服务的方法,重点的协议判断转发,也在这里面其实,你理解后就会觉得很简单,核心步骤:判断 -> 转发 -> 响应。我们改变了前两步的默认逻辑,仅此而已Client在 simple_http_server 目录下新建 client.go,写入文件内容:package mainimport ( “context” “log” “google.golang.org/grpc” “github.com/EDDYCJY/go-grpc-example/pkg/gtls” pb “github.com/EDDYCJY/go-grpc-example/proto”)const PORT = “9003"func main() { tlsClient := gtls.Client{ ServerName: “go-grpc-example”, CertFile: “../../conf/server/server.pem”, } c, err := tlsClient.GetTLSCredentials() if err != nil { log.Fatalf(“tlsClient.GetTLSCredentials err: %v”, err) } conn, err := grpc.Dial(”:"+PORT, grpc.WithTransportCredentials(c)) if err != nil { log.Fatalf(“grpc.Dial err: %v”, err) } defer conn.Close() client := pb.NewSearchServiceClient(conn) resp, err := client.Search(context.Background(), &pb.SearchRequest{ Request: “gRPC”, }) if err != nil { log.Fatalf(“client.Search err: %v”, err) } log.Printf(“resp: %s”, resp.GetResponse())}验证gRPC Client$ go run client.go 2018/10/04 14:56:56 resp: gRPC HTTP ServerHTTP/1.1 访问总结通过本章节,表面上完成了同端口提供双服务的功能,但实际上,应该是加深了 HTTP/2 的理解和使用,这才是本质拓展如果你有一个需求,是要同时提供 RPC 和 RESTful JSON API 两种接口的,不要犹豫,点进去:gRPC + gRPC Gateway 实践问题你以为这个方案就万能了吗,不。Envoy Proxy 的支持就不完美,无法同时监听一个端口的两种流量 ????参考本系列示例代码go-grpc-example系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server带入gRPC:gRPC Streaming, Client and Server带入gRPC:TLS 证书认证带入gRPC:基于 CA 的 TLS 证书认证带入gRPC:Unary and Stream interceptor带入gRPC:让你的服务同时提供 HTTP 接口带入gRPC:对 RPC 方法做自定义认证带入gRPC:gRPC Deadlines ...

October 13, 2018 · 2 min · jiezi