用ASP.NET Core 2.1 建立规范的 REST API -- HATEOAS

本文所需的一些预备知识可以看这里:  用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 和  用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) + 准备项目

建立Richardson成熟度2级的POST、GET、PUT、PATCH、DELETE的RESTful API请看这里:用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST, 用ASP.NET Core 2.0 建立规范的 REST API -- DELETE, UPDATE, PATCH 和 Log和用ASP.NET Core 2.1 建立规范的 REST API -- 翻页/排序/过滤等

本文将把WEB API项目开始提升到Richardson成熟度3级的高度,尽管暂时还没有实现REST所有的约束,但是已经比较RESTful了。

本文需要的代码(右键另存,后缀改为zip):https://images2018.cnblogs.com/blog/986268/201806/986268-20180608085054518-398664058.jpg

HATEOAS(Hypermedia as the engine of application state)是 REST 架构风格中最复杂的约束,也是构建成熟 REST 服务的核心。它的重要性在于打破了客户端和服务器之间严格的契约,使得客户端可以更加智能和自适应,而 REST 服务本身的演化和更新也变得更加容易。

HATEOAS的优点有:

具有可进化性并且能自我描述

超媒体(Hypermedia, 例如超链接)驱动如何消费和使用API, 它告诉客户端如何使用API, 如何与API交互, 例如: 如何删除资源, 更新资源, 创建资源, 如何访问下一页资源等等. 

例如下面就是一个不使用HATEOAS的响应例子:

{"id" : 1,"body" : "My first blog post","postdate" : "2015-05-30T21:41:12.650Z"
}

如果不使用HATEOAS的话, 可能会有这些问题:

  • 客户端更多的需要了解API内在逻辑

  • 如果API发生了一点变化(添加了额外的规则, 改变规则)都会破坏API的消费者.

  • API无法独立于消费它的应用进行进化.

如果使用HATEOAS:

640?wx_fmt=png

这个response里面包含了若干link, 第一个link包含着获取当前响应的链接, 第二个link则告诉客户端如何去更新该post.

 

Roy Fielding的一句名言: "如果在部署的时候客户端把它们的控件都嵌入到了设计中, 那么它们就无法获得可进化性, 控件必须可以实时的被发现. 这就是超媒体能做到的.

针对上面的例子, 我可以在不改变响应主体结果的情况下添加另外一个删除的功能(link), 客户端通过响应里的links就会发现这个删除功能, 但是对其他部分都没有影响.

HTTP协议还是很支持HATEOAS的:

如果你仔细想一下, 这就是我们平时浏览网页的方式. 浏览网站的时候, 我们并不关心网页里面的超链接地址是否变化了, 只要知道超链接是干什么就可以.

我们可以点击超链接进行跳转, 也可以提交表单, 这就是超媒体驱动应用程序(浏览器)状态的例子.

如果服务器决定改变超链接的地址, 客户端程序(浏览器)并不会因为这个改变而发生故障, 这就浏览器使用超媒体响应来告诉我们下一步该怎么做.

那么怎么展示这些link呢? 

JSON和XML并没有如何展示link的概念. 但是HTML却知道, anchor元素: 

<a href="uri" rel="type"  type="media type">

href包含了URI

rel则描述了link如何和资源的关系

type是可选的, 它表示了媒体的类型

为了支持HATEOAS, 这些形式就很有用了:

640?wx_fmt=png

method: 定义了需要使用的方法

rel: 表明了动作的类型

href: 包含了执行这个动作所包含的URI.

 

为了让ASP.NET Core Web API 支持HATEOAS, 得需要自己手动编写代码实现. 有两种办法:

静态类型方案: 需要基类(包含link)和包装类, 也就是返回的资源里面都含有link, 通过继承于同一个基类来实现.

动态类型方案: 需要使用例如匿名类或ExpandoObject等, 对于单个资源可以使用ExpandoObject, 而对于集合类资源则使用匿名类.

 

使用静态基类包装类

 首先建立一个LinkResource,表示链接:

640?wx_fmt=png

再建立一个抽象父类 LinkResourceBase:

640?wx_fmt=png

它只有一个属性Links。

然后我让CityResource继承于LinkResourceBase:

640?wx_fmt=png

最后在Controller里面,我们需要写代码来为资源创建上面概念提到的Links。这里也需要用到UrlHelper,需要在Controller里面注入。

640?wx_fmt=jpeg

由于我要为Resource创建很多基于路由的链接地址,所以需要为相关Action的路由填上名字:

640?wx_fmt=png

然后在Controller里面建立一个方法,它可以为CityResource添加需要的Links,并返回处理后的CityResource。

640?wx_fmt=png

首先为资源添加的是本身的链接,这里使用UrlHelper和路由名以及cityId作为参数可以得到href,难道不需要传递countryId吗?因为Controller的路由地址已经包含了countryId参数,UrlHelper会自动处理这个问题的;而rel的值可以自行填写,这里我用self来表示本身,API消费者需要知道这部分,通过rel的值,API消费者就会知道API提供了哪些功能;最后method的值是GET。

其它几个链接也是类似的。根据需要你可以添加额外的链接,但是针对本文这个简单的例子,这些链接就够了。

接下来要做的就是保证每当CityResource被Action返回的时候,都会执行该方法来创建相关的链接

首先考虑返回单个City的情况,GET:

640?wx_fmt=png

POST也是一样的:

640?wx_fmt=png

还有一个GetCitiesForCountry这个方法,它返回的资源的集合,所以我需要遍历集合,在每一个资源上调用该方法:

640?wx_fmt=png

这里只需要使用Select方法即可,它本身就是遍历。

测试,首先是GET单个City:

640?wx_fmt=png

看起来是OK的,然后在用里面的链接测试相关操作也是好用的,我就不贴图了。

下面测试一下POST:

640?wx_fmt=png

结果也是OK的,链接都是好用的。

最后看一下集合的GET:

640?wx_fmt=png

看起来还不错,集合里的每个资源都有正确的链接。但是结果里并不存在针对整个集合的链接。我们也可以直接把结果改变成这个样子

{value: [city1, city2...]links: [link1, link2...]    
}

因为这是不合理的JSON结果,它并不是被请求的资源的类型。

 

暂时先不管这点,为了支持集合的HATEOAS,我们需要一个包装类:

640?wx_fmt=png

这个类可以看作是针对某种类型的特殊集合,它继承于LinkResourceBase,具有链接的属性;此外还要保证T的类型也是LinkResourceBase,这样就可以保证返回的集合里面的元素也都有Links属性;这个类只有一个Value属性,类型是IEnumerable<T>。

 

回到Controller再创建一个方法叫CreateLinksForCities:

640?wx_fmt=png

 

 640?wx_fmt=png

注意参数和返回类型都是LinkCollectionResourceWrapper。

最后在GET Action方法里调用该方法即可:

640?wx_fmt=png

 

测试:

640?wx_fmt=png

结果是可以的,现在对于CityResource来说差不多可以说是支持HATEOAS了。

 

使用动态类型

这里要用到dynamic和匿名类型。

现在CountryController里面的GET方法返回的是IEnumerable<ExpandoObject>,是塑形后的CountryResource:

640?wx_fmt=png

我无法把这种对象继承于某种父类以便添加Links属性。所以这种情况下,就需要使用匿名类的方式。

这里也是分单个资源和集合资源两种情况。

单个资源

首先为路由添加好名称:

640?wx_fmt=jpeg

由于ExpandoObject无法继承我定义的父类,所以只好建立一个方法返回Links:

640?wx_fmt=png

由于数据塑形的存在,参数还要加上fields。前面几个链接很好理解就是Country资源的相关链接,而后两个资源是Country资源的子资源City的,分别是为Country创建City和获取Country下的Cities。

这个方法表明的我们已经是在驱动应用程序的状态了。这也就是HATEOAS的亮点。

然后就把这些links添加到响应的body即可。首先是GET方法:

640?wx_fmt=png

返回Links,为ExpandoObject添加一个links属性,并返回即可。

测试:

640?wx_fmt=png

OK。然后我们添加几个数据塑形的参数:

640?wx_fmt=png

仍然OK, self的Link里面的href也带着这些参数。

 

然后是POST Action的方法:

640?wx_fmt=jpeg

和GET差不多,只不过POST不需要数据塑形。注意返回的CreatedAtRoute里面的第二个参数里面的id,我是从linkedCountryResource里面取出来的,而不是countryModel的id,这样做也许更好,因为这个id应该是linkedCountryResource里面的。

测试:

640?wx_fmt=png

结果也是OK的。

集合资源

之前我们对GetCountries做了翻页的处理,并且把翻页的元数据放在了响应的Header里面,并且里面包含了前一页和后一页的链接:

640?wx_fmt=png

其实这两个链接放在Links集合里是更好的,所以下面这个方法会添加前一页和后一页的链接:

640?wx_fmt=png

 这里使用了之前创建的CreateCountryUri方法,分别返回了self和前一页以及后一页。

最后在GetCountries方法里调用:

640?wx_fmt=png

首先把元数据里面的两个链接去掉了。

然后为集合创建了links,再然后对集合进行数据塑形,并把集合里面的每个对象都加上了links。最后返回一个包含value和links的匿名类。

测试:

640?wx_fmt=png

640?wx_fmt=png

正确的返回了结果。

下面测试一下各种参数:

640?wx_fmt=png

640?wx_fmt=png

结果应该是OK的,但是大小写貌似有一些问题,这个我直接在源码里面改吧。

 

这里介绍了两种方法,其实在项目中根据情况还是使用一种比较好。

 

Media Type

针对响应的结果,其描述性的数据或者叫元数据应该放在Header里面。例如之前做翻页的时候,总页数,当前页数等数据都放在了Header里面;而下一页和上一页的链接则放在了响应的body里面。那这两个链接应该是资源的一部分吗?或者说他们是否对资源进行了描述(是否是元数据)?其它的链接也存在这个问题。如果是元数据,那么就应该放在Header,如果是资源的一部分,就可以放在响应的body里。现在的情况是,上例和之前的写法是对同一种资源的不同表述。但是到目前我们请求的Accept Header都是application/json,也就是想要资源的JSON表述,但是返回的并不是Country资源的表述,而是另外一种东西,它在Country资源的JSON表述的基础上还拥有links属性,所以说如果我们请求的是application/json,那么links就不应该是资源的一部分。

实际上现在返回的东西是另一种media type而不是application/json,这样我们就破坏了资源的自我描述性这条约束每个消息都应该包含足够的信息以便让其它东西知道如何处理该消息)。所以我们返回的content-type的类型是错误的,而且还会导致API消费者无法从content-type的类型来正确的解析响应,也就是说我没有告诉API消费者如何来处理这个结果。那么解决方案就是创建新的media type。

Vendor-specific media type 供应商特定媒体类型

它的结构大致如下:

application/vnd.mycompany.hateoas+json

 

第一部分vnd是vendor的缩写,这一条是mime type的原则,表示这个媒体类型是供应商特定的。

接下来是自定义的标识,也可能还包括额外的值,这里我是用的是公司名,随后是hateoas表示返回的响应里面要包含链接。

最后是一个“+json”。

整个这个media type就表示我所需要的资源表述是JSON格式的,而且还要带着相关链接。

所以当请求的media type是application/json的时候,只需要返回资源的JSON表述。

而请求application/vnd.mycompany.hateoas+json的时候,需要返回带有链接的资源表述。

修改Action方法:

640?wx_fmt=png

640?wx_fmt=png

使用FromHeader读取Header里面的Accept的值,然后判断如果media type是自定义的,那么就是包含链接的结果;否则,就使用不包含链接的结果,并且把翻页相关的链接放在自定义的Header里面。

测试:

640?wx_fmt=png

请求application/json,返回结果不带links。

修改media type:

640?wx_fmt=png

返回的是406,Not Acceptable。

这是因为ASP.NET Core的格式化器并不认识我们这个自定义的媒体类型。

在Startup里面添加这两句话以支持这个媒体类型:

640?wx_fmt=jpeg

然后再测试:

640?wx_fmt=png

640?wx_fmt=png

现在就对了。

 

根文档

RESTful的API需要为API的消费者提供一个根文档。通过这个文档,API消费者可以知道如何与其余的API进行交互。可以把这个理解为索引页面吧。

这个文档位于API的根部,建立一个RootController:

640?wx_fmt=png

它的路由地址就是根路径/api。

它只有一个GET方法,通过读取Header里的Accept的值,来返回相应的链接。

这里如果媒体类型是我之前自定义的那个,就会返回三个链接:本身,获取Countries,创建Country。这三个就足够了,有了这三个链接,其它的操作和资源(City)的路由地址都会通过一层层的链接获得到。

如果请求类型是其它的,就返回204。

由于我这个程序太简单了,所以这里只写这些内容就足够了。

 

现在,关于资源的表述以及媒体类型你可能会发现更多的问题。

看之前的例子里面的Links链接,这些链接的格式并不是某个标准的格式,而是我自己创建的格式,消费者API并不知道如何处理这些Link,消费者API需要从API文档中了解如何解析Link,我需要在API文档里描述rel的值。

我们也知道媒体类型media type也是API的对外接口合约的内容。这里还有另外一个问题,超媒体允许程序控件、链接等在被需要的时候提供,针对某个动作的链接,API消费者并不知道应该在请求里放什么内容。

之前我们已经创建了自定义的媒体类型,回忆一下Country的GET和POST两个Action,它们使用的是不同的ResourceModel:

640?wx_fmt=png

640?wx_fmt=png

尽管我的例子里它们的属性很像,但是它们是不同的Model,并且有可能属性差别很大。

然后在两个Action里,我都是用的是application/json这个媒体类型,实际上这个项目里目前大部分的API我都是用的是application/json。但是实际上这两个Model是对Country这个资源的不同表述,使用application/json实际上是错误的。应该使用vendor-specific的媒体类型,例如:

application/vnd.mycompany.country.display+json和application/vnd.mycompany.country.create+json。根据情况也可以做的更细更灵活一些。这样API消费者多少知道了针对不同动作应该发送什么样的请求内容了。

 

版本

我们的API到现在已经更改了很多次,API肯定会变化,所以需要版本的介入。

API的功能,业务逻辑,甚至Resource Model都会发生变化,但是我们需要保证变化的同时不要对API的消费者造成破坏。

进行版本控制的办法有几个:

  • 在Uri里面插入版本:/api/v1/countries

  • 通过query string 查询字符串:/api/countries?api-version=v1

  • 自定义Header:例如:”api-version“=v1

但是在RESTful的世界里,这些做法不是都可以的。

实际上Roy Fielding建议不要对RESTful API进行版本管理

但是实际上很多人感觉还是需要对API进行版本管理的,因为需求肯定会一直变化的,API就会一直变化。但是也不要对任何东西都进行版本管理,我们应该尽量小心的使用版本,尽量使API向下兼容

 

如果API的功能或业务逻辑变化了,HATEOAS会把这件事处理很好, API的消费者通过观察HATEOAS的这些东西,就不会对它造成破坏。

但是如果Resource Model变化了,这确实是个问题,Roy Fielding说这种情况也不应该进行版本管理

这些其实就是之前的问题,我如何让API的消费者知道资源的表述应该是什么样的;还有我如何保证随着API的进化,API的消费者也会跟着进化?

根据Roy Fielding的阐述,这些问题的解决方案就是使用按需编码约束(Code on Demand)来适配媒体类型和资源表述的进化,约束中提到API可以扩展客户端的功能。

也许在ASP.NET MVC或者一些web网站可以自适应这种变化,如果这些网站的js,html等是从服务器端生成的;但是大多数的时候,其实很难实现这种自适应变化。

 

我们也许可以在媒体类型里添加版本号来适当处理资源表述的变化。例如:

application/vnd.mycompany.country.display.v1+json和application/vnd.mycompany.country.display.v2+json

下面举个例子, 我在Entity Model里面添加了一个新的属性大洲 Continent,当然它是可空的:

640?wx_fmt=png

而现在API的消费者可以在创建Country的时候给Continent赋值也可以不赋值,这时,就需要再创建一个带有Continent属性的ResourceModel为POST这个动作:

640?wx_fmt=png

别忘了做AutoMapper的映射配置。

在Controller里,针对POST动作它的参数类型可能是CountryAddResource和CountryAddWithContinentResource,所以还需要再建立一个POST的方法:

640?wx_fmt=png

由于有了两个路由地址一样的POST方法,所以还需要根据Content-Type这个Headerd的值来决定请求进入哪个方法。这里我们可以自定义一个应用于Action方法的自定义约束属性标签:

640?wx_fmt=png

这个很简单,传进来需要匹配的header类型,和值(允许多个值);然后从request的headers里面找到匹配即可返回true。

分别应用到两个Action:

640?wx_fmt=png

640?wx_fmt=png

最后还需要把这两个媒体类型注册一下,注意这两个是输入:

640?wx_fmt=png

 

下面测试,首先使用原来的application/json:

640?wx_fmt=png

404,没错,因为Content-Type已经不符了。

接下来使用原来的POST方法的媒体类型:

640?wx_fmt=png

就会进入原来的POST方法:

640?wx_fmt=jpeg

 

使用另一个媒体类型,就会进入另外一个方法,就不贴图了是好用的。

 

上面的自定义约束标签RequestHeaderMatchingMediaTypeAttribute的第二个参数meidatypes是个数组,为什么?

因为,就看上一个截图,这个方法接收的格式是json,但是如果我想要也支持接收xml,就直接在数组里添加另一个xml的媒体类型就可以了。

 

这个约束标签不仅仅可以过滤一个Header类型,也可以多个,比如说我同时还要根据Accept Header来指定不同的方法,那么:

640?wx_fmt=jpeg

这里提示重复,但是可以通过修改这个约束标签类来解决:

640?wx_fmt=png

这时,错误提示就没有了:

640?wx_fmt=png

 

微软的API Versioning库

微软提供了一个API 版本管理的库:Microsoft.AspNetCore.Mvc.Versioning

使用Nuget安装后,在Startup里面注册:

640?wx_fmt=png

随后就需要在Controller上标注版本了:

640?wx_fmt=png

实际上我并不是很喜欢这种版本管理,感觉会很乱。。有兴趣的话,请看一下官方文档吧:

https://github.com/Microsoft/aspnet-api-versioning/wiki/New-Services-Quick-Start

随后我把这个库删掉了。  

除了手动实现的这种HATEOAS,还有很多其它的选项,例如OData。但是OData就不仅仅是HATEOAS了,它正在尝试对RESTful API进行标准化,例如它还对创建Uri、翻页以及调用方法等等都制定了很多规则,还有很多的东西,但是我还是不怎么使用OData。 

这次就写到这里,源码在:https://github.com/solenovex/ASP.NET-Core-2.0-RESTful-API-Tutorial

下周继续。

相关文章:

原文地址: https://www.cnblogs.com/cgzl/p/9153749.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/321182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学习手记(2020/8/19~2021/3/19)

文章目录所有集合子集数量和结论证明枚举子集的方法最大匹配模的次数线性基卡特兰数树形dpTipTipTip斯特林数斐波那契幂前缀和hallhallhall定理阿巴阿巴1狄利克雷卷积常用式子组合数学恒等式竞赛图性质一些博弈模型基础反演二项式反演莫比乌斯反演欧拉反演子集反演min-max\text…

奶牛的交叉路(jzoj 1756)

奶牛的交叉路 题目大意&#xff1a; 有两个数轴&#xff0c;之间有n条线分别连接着两个数轴的点各一个&#xff0c;这些线可能会相交&#xff0c;问有多少条线没有和其他点相交 样例输入 4 -3 4 7 8 10 16 3 9样例输出 2数据范围限制 1<N<100000&#xff0c;-1…

.Net Core 环境下构建强大且易用的规则引擎

1. 引言1.1 为什么需要规则引擎在业务的早期时代&#xff0c;也许使用硬编码或者逻辑判断就可以满足要求。但随着业务的发展&#xff0c;越来越多的问题会暴露出来&#xff1a;逻辑复杂度带来的编码挑战&#xff0c;需求变更时改变逻辑可能会引起灾难重复性的需求必须可重用&am…

【最短路】【SPFA】单源最短路径 (luogu 3371)

单源最短路径 luogu 3371 题目大意&#xff1a; 求出一个点到其他点的最短路 原题&#xff1a; 题目背景 本题测试数据为随机数据&#xff0c;在考试中可能会出现构造数据让SPFA不通过&#xff0c;如有需要请移步 P4779。 题目描述 如题&#xff0c;给出一个有向图&…

在Windows 下如何使用 AspNetCore Api 和 consul

一、概念&#xff1a;什么是consul:Consul 是有多个组件组成的一个整体&#xff0c;作用和Eureka,Zookeeper相当&#xff0c;都是用来做服务的发现与治理。 Consul的特性&#xff1a;1、 服务的发现&#xff1a;consul可以把注册到其中的服务提供给使用者&#xff0c;也可以主动…

网络流及建模专题(下)

前言 不断更新中… 专题的(下)篇将介绍网络流的一些奇奇怪怪的应用和费用流有关的一些套路。 本专题暂时包含三道题&#xff1a; 洛谷P1251 餐巾计划问题: 费用流的基本应用 Trade Gym - 100212I: 使用网络流对图论中的边进行调整 codeforces 818G - Four Melodies: 费用…

CentOS 7.4 下 如何部署 AspNetCore 结合 consul

上篇我们讲到consul的概念&#xff0c;以及在WIN下如何使用&#xff1a; 在Windows 下如何使用 AspNetCore Api 和 consul步骤如下&#xff1a;1、安装虚拟机VM 2、下载安装 CentOS 7.4&#xff08;地址就不提供了&#xff09;这是安装示例&#xff1a; VM虚拟机安装CentOS 示例…

【SPFA】最优贸易(luogu 1073)

最优贸易 luogu 1073 题目大意&#xff1a; 有n个城市和m条线路连接着这些城市&#xff08;当编号为1时是有向&#xff0c;2时是无向&#xff09;&#xff0c;从1城市出发到n城市&#xff0c;每个城市都有固定的水晶球价格&#xff08;进价和售价一样&#xff09;&#xff0…

Razor Page Library:开发独立通用RPL(内嵌wwwroot资源文件夹)

Demo路径&#xff1a;https://github.com/yanshengjie/RPL.Demo1. IntroductionRazor Page Library 是ASP.NET Core 2.1引入的新类库项目&#xff0c;属于新特性之一&#xff0c;用于创建通用页面公用类库。也就意味着可以将多个Web项目中通用的Web页面提取出来&#xff0c;封装…

Problem H Rock Paper Scissors,FFT

题目 题目链接 题意 给出两段石头剪刀布的顺序SS和T" role="presentation" style="position: relative;">TT&#xff0c;其中TT要短一些,现在让你把T" role="presentation" style="position: relative;">TT往SS的…

.NET Core微服务之基于Ocelot实现API网关服务

一、啥是API网关&#xff1f;API 网关一般放到微服务的最前端&#xff0c;并且要让API 网关变成由应用所发起的每个请求的入口。这样就可以明显的简化客户端实现和微服务应用程序之间的沟通方式。以前的话&#xff0c;客户端不得不去请求微服务A&#xff08;假设为Customers&am…

[译]RabbitMQ教程C#版 - 发布订阅

先决条件本教程假定RabbitMQ已经安装&#xff0c;并运行在localhost标准端口&#xff08;5672&#xff09;。如果你使用不同的主机、端口或证书&#xff0c;则需要调整连接设置。从哪里获得帮助如果您在阅读本教程时遇到困难&#xff0c;可以通过邮件列表联系我们。1.发布/订阅…

用ASP.NET Core 2.1 建立规范的 REST API -- 缓存和并发

本文所需的一些预备知识可以看这里: 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 和 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) 准备项目建立Richardson成熟度2级的POST、GET、PUT、PATCH、DELETE的RESTful API请看这里&#xff1a;用ASP.NET Core …

2017 SEERC Divide and Conquer 树上差分

题目 题目大意&#xff1a;给出两颗树的复合图(即这张图是由两颗树拼起来的)&#xff0c;询问最小割掉多少条边&#xff0c;可以使得图不联通&#xff0c;并输出方案数。 分析 我觉得这是一道很难的题目&#xff0c;因为比较难想&#xff0c;前提结论比较多。 首先我们需要…

青蛙跳荷叶

青蛙跳荷叶 题目大意&#xff1a; 有n个点&#xff0c;从1开始到跳完这些点&#xff0c;且每次的距离不能相等&#xff0c;一个点不能到多次 原题&#xff1a; 题目描述 从前&#xff0c;有一个小青蛙决定去荷叶上练习跳跃.现在有n个荷叶排成一排&#xff0c;小青蛙一开始…

基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现

0.简介0.1 什么是 ConsulConsul是HashiCorp公司推出的开源工具&#xff0c;用于实现分布式系统的服务发现与配置。这里所谓的服务&#xff0c;不仅仅包括常用的 Api 这些服务&#xff0c;也包括软件开发过程当中所需要的诸如 Rpc、Redis、Mysql 等需要调用的资源。简而言之 Con…

【DFS】排排坐

排排坐 题目大意&#xff1a; 有n个方块&#xff0c;有一些是黑色&#xff0c;有一些是白色&#xff0c;可以点击一个方块使它和它旁边的方块反转颜色&#xff08;黑变白&#xff0c;白变黑&#xff09;&#xff0c;问最少要点多少次才能将方块 们 变成目标的方块们&#xff…

用ASP.NET Core 2.1 建立规范的 REST API -- 保护API和其它

预备知识: 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识 (2) 准备项目建立成熟度2级的 API请看这里&#xff1a;用ASP.NET Core 2.0 建立规范的 REST API -- GET 和 POST, 用ASP.NET Core 2.0 建立规范的 REST AP…

华为资深工程师:码农很多,但程序员并不多......

“春节假期&#xff0c;与几位友人小聚&#xff0c;大家互道工作顺利、平安健康云云......期间一位驰骋商界多年的老友问&#xff1a;“你现在在华为做什么工作呀?”我很骄傲地说&#xff1a;”系统架构师“&#xff0c;可是他却愣了很久。但当我老婆在旁边补上一句“码农“时…

VS2017 15.8第二个预览版本提升了对CPU Profiling和F#的支持

VS2017 15.8第一个预览版本的特性包括对ARM64构建的支持、ASP.NET Core对Docker的支持以及重新引入LibMan。在15.8的第二个预览版本中&#xff0c;微软发布了一个新Google Android模拟器的预览功能&#xff0c;它能够与Hyper-V兼容。这样的话&#xff0c;最新的Android模拟器就…