构建你的长寿命的API第1部分:规范驱动的API开发

构建你的长寿命的API第1部分:规范驱动的API开发

         这篇文章是由MuleSoft的Mike Stowe在nginx.conf 2016发布的演示文稿改编的。第一部分重点是规范驱动的API开发。第二部分讨论的最佳实践。你可以查看完整的呈现的记录的YouTube,具体信息如下: 

0:00 介绍 

1:52 API正在改变世界 

2:32 API正在连接一切 

3:36 API应该是持久的 

4:01 构建一个持久的API的5个步骤 

4:38 从长计议 

6:03 你的API是契约 

6:39 仔细想清楚 

7:36 谁将使用你的API? 

8:40 什么是你的API的目的? 

9:41 列出你的用户可能需要做什么 

10:53 你正在建立什么类型的API? 

11:43 SOAP vs RPC vs REST 

12:34 你理解REST约束吗? 

13:42 大多数API不是RESTful 

14:55 为长期构建 

15:20 版本控制是必要的邪恶 

17:51 什么时候需要版本 

18:35 什么时候不需要版本 

19:54 版本控制不会导致糟糕的设计 

21:12 使用规范驱动开发 

22:00 混合方法 

23:11 敏捷设计周期 

24:36 今天的一些API规范 

25:05 RAML优点 

27:25 RAML是什么样子的? 

28:33 规范是 Blueprint 

0:00介绍 
      迈克•斯托:今天我们将谈论构建你的长寿命的API。在过去的十年里,我已经有构建和使用API的机会,同时发现了几个不同的事情。 
         首先是构建API实际上很容易。这真的不是那么难写的代码,或者使用框架比如Grape、Rails API、Jersey、Apigility,或者其他的框架。我意识到的第二件事是,人们并不擅长制作持久的API。让我问大家一个问题:在这里有多少人使用了一个API已经被破坏?所以我们都有同样的痛点。 
         这次谈话的重点是,我们如何才能使一个API变得持久,为什么要变得持久,如何处理API的版本问题,以及我们如何能够确保我们构建什么样API值得我们的用户会喜欢,所以从长远来看,它使得我们不会花费更多的钱用于开发或者支持。 
         我们不打算潜入了大量的代码。相反,我们将专注于原则,最佳实践,以及我们如何建立一个更有效的方法。 
1:52 API正在改变世界


        我想公平地说:API正在改变世界。如果你曾经使用过智能手机,或者即使你曾经开过车,你可能已经使用了API。今天有超过15000公共API 在ProgrammableWeb网站上,它们连接各种各样的设备,但这只是冰山一角。


        如果你想想看,有几十万甚至上百万的API, 连接着mobile apps,以及用于商业用途连接部门机构。 
2:32 API正在连接一切


         今天,API连接了很多不同的东西。显然,他们正在连接我们的移动应用程序,以及连接我们的企业架构,同时也连接我的手机与手表,如Apple Watch和Android手表、眼镜、汽车、冰箱、恒温器、家用机器人等等。


        想一下,所有的东西正在被 API连接着,这是多么重要。 
        想象一下,在你的家里有家庭安全系统,但最糟糕的事情可能发生:有人闯入你的房子,但警报开始关闭,然后说“需要更新”(它无法通知警察,因为API已过期)。这不是一件好事情。 
        如果我们了解微服务,我们想要做的最后一件事是部署700微服务,都依赖于一个API,但是这个API被破坏了——我们要回去更新700微服务,它们可能需要重新进行组成。 
3:36 API应该是持久的


         为了让API更好的工作,为了让物联网更好工作,为了使微服务更好工作,它们需要的API是持久的。


        不仅如此,版本变动或更改你的API的代价是十分昂贵的。这不只是你去说:“我们要重建我们的API或重构我们的代码”,也关系到所有用户或者公司的各部门,因为他们依赖这个API。 
4:01构建一个持久的API的5个步骤 
        值得庆幸的是使用五个简单的步骤,就可以构建持久的API。


        第一步是拥有一个长期的思维模式。这听起来像是常识,但很多人会说:“我们要建立这个API,我们要得到它在那里,我们得到的反馈,然后我们将版本控制它“,但是实际上从来没有这么工作过。第二步了解你正在建设什么。它似乎也是常识,但大多数API是非持久地开发,没有真正了解什么是实际上他们需要构建。第三步利用规范驱动的开发,我们将后面谈到。第四步纳入最佳实践。然后重复步骤1到4。 
4:38从长计议


        你的API是契约。我们忘记了,不像一个敏捷的Web应用程序——我们可以说,“我们将启动它,然后我们可以进行更改“——你不能更改你的API,否则会影响你的用户,造成他们的系统挂掉。版本控制也不是解决方案。 
        我最喜欢的长期思考的理由是,你可能真的存在很糟糕的长期设计。你可能会认为你擅长设计,但让我问你:你曾经看过你的代码一年后,说:“这是最惊人的事情,我不会改变任何东西?我们真的善于在短期内解决这个问题,但不是长期的。为了使API设计看起来更加安全,你可以支付一点点更多现在或更多更晚,所以你需要考虑事情。 
        思维模式就是一切。你不会希望一个团队走向一场篮球比赛,“我们要输了,我们会输。”因为很可能,他们不会赢。同理,API设计也是这样。 
6:03你的API是契约


         你的API是契约。我们经常忘记,当我们做出改变(破坏旧功能)时,我们正在夺去消费者赚钱的能力。当我们改变我们的API,我们的客户或我们公司的其他部门必须说:“等等,我不得不停止我做的一切,回去和解决的事情,因为你打破了”。这意味着他们不会添加新功能,他们不会赚更多的钱,他们肯定不会支付你更多的钱,因为他们正在努力解决的东西,而不是获得新的客户。 
6:39仔细想清楚


        在构建API之前,你必须考虑你的API的每个方面。这些方面可能看起来像基本问题和常识,但他们错过了很多。 
         你的API是为了谁?你正在建立什么类型的API?你将如何维护你的API?很多公司都说:“看,我们将要构建一个API,推出它,这就是它。”我回答你,这不是它。你必须维护它,它们将是安全补丁。 
        你如何记录你的API?你要有一个完整的文档团队吗?你要使用Swagger控制台吗?你使用RAML吗?你将如何让用户与你的API进行互动?你将如何处理诸如身份验证、配置、限制、安全风险、DDoS攻击、开发人员无意中使用无限循环等小事?你要如何管理支持? 
        这些不必是真正艰难的讨论,但你必须有。你的支持方法可能是有一个专门的API支持团队,或者有工程师将提供支持,或有一个社区论坛,或者[你甚至可以说]“我们真的不是投资在API,所以祝你好运; 如果他们需要支持,他们可以弄清楚”。 
7:36谁将使用你的API?


        你的最终用户是谁?它会是现在的客户吗?它会是商业伙伴吗?第三方服务或开发者?这是很重要的回答,因为在你可以构建之前,你必须了解他们是谁,他们需要什么。他们需要访问哪些操作?这意味着从第一天开始涉及你的用户。 
        当我开始作为一个网络开发者,我是一个菜鸟,我有这个摄影客户端。她告诉我,“我在寻找一个美丽的摄影网站,我想要这种类型的图像,这种类型的字体。”她给了我的规格,我出去了,我建立了最美丽的摄影网站,在我看来。我把它送给她,她看着它,说:“这是什么?你使用深色,我做婚纱摄影,我想要淡色。你使用这个图像,我想要那个图像。你使用这种字体,我想要那个字体。我把我的心倒入这个网站,但结果是她讨厌它,因为我没有让她参与设计过程。我没有问她,她想要什么样子的, API设计也是这个道理。 
8:40你的API的目的是什么?


        在MuleSoft时候,我们处理很多企业客户,他们说,“嘿,我们想要一个API。”我们回答,“嗯,这是伟大的,但你的API是什么?”他们说,“我们将与世界分享我们的服务和数据。“所以我们问他们,”好吧,但他们想要什么数据?他们将要访问什么服务?他们将如何与你现有的服务或其他API进行交互?他们如何与来自第三方的其他API或服务进行交互,他们可能需要与你的服务一起使用?他们需要什么行动来处理? 
         一个我们碰到的陷阱是说,“我们要建立的REST API,我们要做的CRUD,我们要做的HTTP方法。”我们开始思考POST,PUT,PATCH,DELETE马上。退一步说,“他们(我们的客户)需要能够添加用户,编辑用户,可能检索密码,发送消息”。 
         这下一点是为房间里的工程师,因为让我们老实说,我们是可怕的:只做必要的。不要喜欢你的API。 
9:41列出你的用户需要做什么


        这是一个非常简单的方法来组织一个API。我们只是说,“我们要有用户,然后你就可以创建一个用户,编辑用户等等。我们将有一个消息系统; 这里是我们的消息,和我们需要的行动。我们将有产品,所以我们需要能够审查一个产品,添加一个产品到购物车。我们要有一个购物车,所以我们需要建立一个结帐。 
        当我们经历这个过程,一些事情开始发生。你注意到的第一件事是,我们已经定义了我们的资源,甚至没有尝试。我们有一切为用户的东西,这是用户的资源。所有这些东西的消息[是]的信息资源。 
你会注意到的另一件事是,我们有重复。我们有信息的用户,并发送一条消息。那么,它可能没有任何意义实际上有一个专门的资源下,用户发送一条消息,这似乎多余。 
          我们所做的是有机会来看看这个问题,“哪里不属于?”我们可以说,“ 发送消息和邮件用户在所属的消息 ”,而且还开发这些两者之间的关系纽带。所以,不要回头说,“让我们创建一个超媒体地图并将所有这些事情联系在一起”事实之后,我们已经创建了一个超媒体地图。我们已经知道,当我们拉起一个用户时,我们需要向他们发送消息的能力,这将是一个链接。 
10:53你正在建立什么类型的API?


        你正在建立什么类型的API?你要构建一个REST API,一个部分REST API,SOAP,RPC?让我问你:什么是正确的类型的API构建?答案是:它取决于它的依赖。 
        很多人会说“REST!”如果你是一个大型企业,并且所有的客户都是传统客户,他们没有准备好REST API,但他们需要SOAP,因为他们有SOAP库,它可能使感觉使用SOAP。这就是为什么Salesforce最长时间维护SOAP API和REST API,因为这是他们的客户要求。 
        为什么要以该格式构建API?你理解你选择的格式的优缺点吗?对你的开发时间意味着什么?对用户的可用性意味着什么?长寿是什么意思?它将如何影响你长期? 
11:43 SOAP vs RPC vs REST


         对于SOAP,我们可以说,“哦,SOAP,这是过时了。这是这个庞大的SOAP封装需要SOAP库,它只是XML。“但是SOAP有WSDL,其中有些人喜欢,它可以是状态或无状态,这是一种不错的。 
         RPC,在另一方面,是真的,真的好用。它返回统一格式- JSON-RPC或XML-RPC -但真的没有上线管道得到它。我把这个网址,我得到这个回来,并做了GET或者POST,这就是它。 
         最后还有REST,它具有所有这些巨大的利益,但有一些注意事项用它。这对于开发人员来说也有点难度,特别是当你开始整合超媒体等东西时,因为对链接进行硬编码总是比实际动态使用更容易。 
12:34你了解REST约束吗?


        假设你要构建REST API,因为你的目标是实现长久,灵活和敏捷。你真的明白什么是REST吗?你是否明白客户端和服务器必须能够单独演进,你对移动设备所做的更改不应该影响服务器,或者客户端不应该影响服务器? 
        你明白REST是无状态的吗?你不在服务器端存储状态。当然,客户端可以存储状态,但是你不保留他们正在做的调用的记录。所以如果我在这里连续两个电话,你不记得第一个电话。你不在乎第一个电话。你自己接受每个电话。 
        它是可缓存的,这意味着你不仅在你的最后提供缓存,但你告诉你的用户,“这里是如何缓存此页面”或“这里是你可以做什么缓存此页面,这里的时间限制”或 ”这将被缓存在所有层”。 
13:42大多数API不是RESTful

 
        创建REST理论的Roy Fielding注意到有一天没有人使用超媒体。他继续问:“有手册坏了吗?文档是否错误?我做错了什么,使它不清楚,如果它没有超媒体,它不是REST? 
这是否意味着你必须构建一个完整的,完全RESTful API?绝对不。你必须构建满足你需求的API,但是你需要了解为什么。 
        有一个公司正在构建一个API的例子,他们说,“我们要构建REST。”他们开始使用它,然后随着时间的推进,很快他们说,“嗯,我们将让他们给我们发送HTML,然后我们将发送他们的JSON。”然后是,“我们要做POST,但他们将通过查询字符串发送数据。他们开始调整事情,因为他们认为,“这将使事情更容易和更好的为我们的用户”,没有理解他们正在需要什么的构建API。他们实际上创建了更多的问题,需要更多的迭代,并花了很多钱试图构建这个API。 
         再次,了解为什么要构建一种类型的API而不是另一种。如果你不这么做,你将付出更多的道路,最终有一个API是一种混合了所有的东西,是不会工作。 
14:55为长期构建 
        这也意味着构建你的API超越今天,这是我们遇到的挑战。今天我们说,“这是API,这些是我们需要的,这是我们的平台”。这是伟大的,但你的平台二年后将是什么样呢?三年?五年?


       我们真的,真的很好的短期设计。但正如Roy Fielding所说,我们对长期设计感到恐惧。 
15:20版本化是一个必要的邪恶


         这使我们进行版本控制,版本化是一个必要的邪恶。在短期内,很容易说“我们要版本,它会是伟大的”。同时,版本也有缺点。


        首先,版本控制会产生向后兼容性,这意味着当你升级API时,依赖于你的API的应用程序将会断开。当它们遭到破坏,你会听到投诉。 
         第二是有多个服务需要维护和多个系统需要支持。你可以说,“不,不,不 —— 这不是它的工作原理。我们将弃用这个API,我们不会再支持它了,我们要发布一个新的API”。 
嗯,这听起来很大的理论,直到有一个安全风险,你必须修补这个安全漏洞。或者你得到那个一百万美元的客户说:“你知道吗?实际上,我们坚持使用旧的API,我们不能升级,我们需要这个功能“或者公司的另一个部门说”我们需要它”。 
         在MuleSoft之前,我有机会作为Constant Contact的开发者传道人,我的整个工作是让人们从版本1升级到版本2。在这一年我在那里,我得到了,我想四人升级他们的API。他们认为,“这一个工作。为什么我们应该回去花钱来解决没有被破坏的东西? 
         另一个问题是版本控制在开发人员之间造成混乱。 
         在Constant Contact,有一天我打电话,那个行上的绅士说:“我有一个问题:API不工作。”我问他:“你使用什么版本的API?”他说,你的。 
         好吧,没问题,有时人们会困惑他们使用的是哪个版本。所以我挖的更深,“它是如何工作的?”他回答说:“嗯,我送POST,我得到的数据了。”我说:“好吧,你得到的XML或JSON?”他回答:“是的“。事实证明,他接管了另一个刚刚说“这没有成功”的开发人员。他呼吁找出为什么它不工作,但他没有做家庭作业,他不知道我们使用什么版本的API。它花了大约20分钟只是要弄清楚他是什么版本。原来他是在版本2,但他需要的功能是版本1,这真的把他气坏啦,因为他们刚刚从版本1升级到版本2。 
        因此,开发人员的采用几乎是不可能的; 你最终会造成混乱,不得不支持新的和旧的API。 
17:51什么时候你需要版本


       当你具有向后不兼容的平台更改时,你需要对API进行版本化。在这种情况下,更改API以反映你现在正在做的新事物可能是有意义的。或者,如果你以前是一家汽车店,现在你是一家面包店,更改你的API是有意义的。 
         第二个实例是当你的API不再可扩展时。换句话说,你搞砸了。你认为你有完美的API,但你不断变化和扩展,不断变化和改进,然后你被卡住,现在你有麻烦。 
        最后,如果你的规格是过时的。也许[你的客户]现在想要JSON,但你仍然使用SOAP与XML,所以你需要更新你的API。 
18:35什么时候不需要版本


        你不应该因为你添加新的端点或在响应中添加了其他数据而对API进行版本化。有关于严格模式的论点。除非你正在做一些需要严格模式的事情,否则我会避免说“这是数据响应的严格模式”。 
        你不应该版本,因为你改变了技术(你从Java到Ruby)。或者如果你更改了应用程序服务。你应该能够更改代码,你应该能够更改你的服务,你应该能够更改依赖关系,而不会影响API的接口。 
        这是我们看到的另一个挑战,顺便说一句,公司说“你可以基于你的数据库生成一个API。我们将根据你的数据库名称创建API“。如果你创建一个独特的接口,那么实际上是可以的 -——如果你回去,你开始改变所有这些,并将它从数据库中解耦。问题是,一旦你说“好吧,我们使用这个数据库。我们将移动到CRM“。所有这些名称都是严格对齐到数据库。。同样的事情,如果你说“看,我们要有一个CRM,我们将通过我们的系统代表一个CRM。”如果你使用特定的API或特定的系统和你的API的顶部紧密耦合,第二个你改变它们,破坏接口,这是打破了向后兼容性。 
19:54版本控制不会导致糟糕的设计 

        版本控制不能原谅可怜的设计。这非常,非常重要的需要指出:版本控制不是一个解决方案。它实际上可能在路上产生更多的问题。



        设计不佳的API将花费你远远,远远长于短期内可能给你带来的好处。 
        我不自豪的一个故事是我有机会加入MuleSoft作为承包商工作,我们建立了这个API。我们花了六个月的时间编写代码,我们构建了这个完美的API。它遵循所有最佳做法和最新标准; 代码是完美的。然后经过六个月的工作,我们发布了这个API,三个星期后,我们意识到没有人使用它。这是因为它不是最佳实践 ——我们有一个伟大的基于REST的API。这只是因为它不能满足他们的需要。 
        我们花了接下来的三个月试图挽救这个API,试图解决它。但是我们最终终于把它扔掉了。这是9到10个月的工作完全被抛弃,因为我们的API不能满足我们的客户的需求。所有因为我们没有花两个星期在开始问我们的用户,我们没有涉及我们的用户,了解他们的需求。 
21:12使用规范驱动的开发


        我们能够避免的方法是使用一种叫做规范驱动开发。 
         规范驱动开发的工作方式是我们首先要做的是在我们编写任何代码之前定义我们的API。我们还将引入设计模式和代码重用。我们对所有的应用程序这样做,所以为什么我们不使用我们的API设计? 
我们将模拟我们的API,并在我们编写单行代码之前获取用户的反馈。我们进行必要的更改。然后我们开始编码到规范,而不是偏离。我们的规范成为真理的源泉。 
         我们最终得到的是一个两阶段的敏捷过程。我们有一个敏捷设计过程来开发契约,然后是一个敏捷开发过程,你可以使用测试驱动开发来实际开发代码。这样做的原因是,这种方式没有进入代码或设计,我们没有测试,我们不确定。 
22:00混合方法


        所以首先我们要设计我们的API,模拟它,获得用户反馈,进行任何更改,测试它,确保它是完美的,然后我们将进入开发。 
        如果你遇到一个问题在开发中,你[实现]这是一个好主意在理论上,但它不工作会发生什么?你只需回到设计过程,在你的测试中解决它,确保它的工作,测试它与用户,然后向前。你现在正在做的是创建一个API,不仅你的用户会喜欢,这将满足他们的需求,你要测试现实世界的用例,而且没有设计缺陷。 
如果你不这样做,会发生什么?有一个社交媒体巨头的API,他们有一个资源,如果你想按年龄搜索,你必须把年龄在JSON中的一种方式。如果你想按位置搜索,这是一个完全不同的方式。如果你想按年龄和位置搜索,它是第三种方式。这不是因为某些依赖关系。这是因为他们有不同的开发者工作,他们没有意识到,他们有这个设置。 
23:11敏捷设计周期


        敏捷设计周期是什么样的?设计API。在你模拟它后获取用户反馈。验证:“这是否满足我们的需求?它满足我们用户的需求吗?它是一个好的设计吗?“如果不是,继续工作,直到你的设计完善,然后当你准备好,进入编码阶段。 
         这里的目标是让我们的开发人员无所畏惧地发展。我们不想让我们的开发人员看看API,并且说“我们正在构建这个资源,并有你的问题。你是如何设置这些数据的?你使用骆驼盒还是蛇盒?你使用一个PUT或一个PATCH来处理?数据是否包含在数据对象中?我们如何区分这一点?“你不想遇到这些问题。你不想要这些不一致,因为这些将导致你的API中断。 
         这些问题花费代价是非常昂贵的。我们说:“嘿,我们要构建这个API。我们要做的MVC,去更改代码,“和[发现]这是行不通的。好消息是今天,有几个新的规格,将使这非常,非常容易。另一个很好的事情是这些相同的规格处理了你有更多的选择。所以,你不只是在设计你的API,然后扔那了,就像我们做的瀑布 [发展]。 
         现在你有一个规范,可以用来生成你的文档,可以用来生成你的测试驱动开发测试。你有一个规范,可以用来创建SDK和客户端生成器,交互式场景,所有这些不同的东西。 
24:36今天的一些API规范


        首先是RAML这是基于REST的API建模语言。 
        第二个是IO Docs是由Mashery®。这是我在I / O文档上的免责声明:我不会建议使用它,只是因为TIBCO Mashery已经移向Swagger,所以他们真的不是维护I / O文档了。 
        第三个是Swagger或OpenAPI Initiative,这是另一个伟大的和非常受欢迎的规格在那里。 
        第四个 是API Blueprint,当涉及到的文档这是梦幻般的,同时它有一些伟大的工具。 
25:05 RAML优点


        我是RAML的粉丝的几个理由。使用RAML,你可以在几行文本中定义API。你可以看到你的API的样子,与API设计相比较。你可以快速为你的API设计原型,供开发人员试用。这意味着,通过点击按钮,你可以将此API发送到世界各地,并让你的开发人员实时地尝试。 
        当你发现设计缺陷时,你可以快速做出调整和更改。你可以轻松地记录你的API。你可以让开发人员在线试用你的API。 
        RAML另一大特点是一种叫做API Notebook。而不是让你的开发商说[给你的客户],“嘿,这里是我们的API。你认为什么?“你可以发送给你的客户一个API Notebook,并且不需要编写任何小代码之外的代码,他们实际上可以测试你的API使用真实的用例,看看它是否满足他们的需求。 
        你可以让开发人员在线试用你的API。你可以让开发人员通过API Notebook与其他API交互。然后,如果你想要SDK,客户端生成器,代码封装器、库、甚至你的构建API的框架,你可以通过开源社区生成这些。如果你正在为大众消费的SDK,但是,我真的会倾向于使用专业的公司像瘦APIMatic.io或REST United,因为无论你使用RAML或Swagger,很多开源的客户端生成器是一种粗略。


        最重要的是,这是我更喜欢RAML超过Swagger的真正原因——你可以使用数据模型和设计模式,这意味着我们说“这些API看起来是我们想要的”。 
         你可以重用代码; 你可以拉入库,这意味着如果你有一个用户对象跨多个API共享,你可以以命名空间的方式拉。你可以创建叠加层,因此你可以有一个规范,然后可以覆盖你的开发环境,你的QA环境,预发和生产[环境]等。 
         在这个例子中,我们希望有一个资源类型collection。对于我们的集合,我们将一次获得多个项目。我们get有一个例子说明。而不是一次又一次地写了这一切,我们不得不说exampleCollection,在为我们拉取Rest。 
27:25 RAML是什么样子?


        RAML是什么样子的?这是我为什么我喜欢RAML,顺便说一句。RAML非常详细,非常复杂[不!]。 
如果我想创建一个资源,我想打电话给一个资源播放列表,我将如何定义在RAML文件里?/playlists,就像我在API上。如果我想添加一个GET到方式播放列表,我怎么想补充一点?get。如果我想有一个200反应,是什么状态代码我用它来把一个200回应?200。 
         所以,你可以看到它很容易开始使用。RAML的好处在于,如果你是一个开发人员,一个科技作家,或者CEO,无关紧要。你甚至可以让你的CEO修改你的API文档; 这取决于你。我不会去过多的深入RAML,但你想深入了解到更多请看raml.org。因此,总的来说,在构建API时,你将需要使用规范,无论是RAML还是Swagger。 
28:33规范是Blueprint


        但记住它不是一个“完成”。 
        一旦开始设计API,每次进行更改时,都要执行这些测试。这样做的原因是,你可以设计完美的基础,但如果你开始不正确地构建在它的顶部,你缩短你的寿命。 
         这篇文章是由MuleSoft的Mike Stowe在nginx.conf 2016发布的演示文稿改编的。第一部分重点是规范驱动的API开发。第二部分讨论的最佳实践(请看后续章节)。你可以查看完整的呈现的记录的YouTube

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值