我正在尝试在我的 ASP rest API 中实现 HATEOAS,将ReferenceResolverProvider.
问题是,根据我使用的控制器,我想使用不同的ReferenceResolvers,因为我需要对每个控制器采取不同的行为。
现在我有通用选项:
services.AddMvc()
.AddJsonOptions(option => option.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver())
.AddJsonOptions(options => options.SerializerSettings.ReferenceResolverProvider = () => new RoomsReferenceResolver<Room>())
.AddJsonOptions(options => options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects);
Run Code Online (Sandbox Code Playgroud)
我想要这样的东西:
services.AddMvc()
.AddJsonOptions(option => option.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver())
.AddJsonOptions<RoomsController>(options => options.SerializerSettings.ReferenceResolverProvider = () => new RoomsReferenceResolver<Room>())
.AddJsonOptions(options => options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects);
Run Code Online (Sandbox Code Playgroud) 我已经搜索了几天关于如何实现包含 HATEOAS 链接 + 使用 Spring boot 和 JPA 分页(无 spring 数据休息)的 Spring REST API,如下随机示例:
\n{\n "_links": {\n "first": {\n "href": "http://localhost:8080/api/albums-list?page=0&size=2&sort=title,desc"\n },\n "prev": {\n "href": "http://localhost:8080/api/albums-list?page=0&size=2&sort=title,desc"\n },\n "self": {\n "href": "http://localhost:8080/api/albums-list?page=1&size=2&sort=title,desc"\n },\n "next": {\n "href": "http://localhost:8080/api/albums-list?page=2&size=2&sort=title,desc"\n },\n "last": {\n "href": "http://localhost:8080/api/albums-list?page=4&size=2&sort=title,desc"\n }\n },\n "page": {\n "size": 2,\n "totalElements": 10,\n "totalPages": 5,\n "number": 1\n },\n "_embedded": {\n "albums": [\n {\n "id": 7,\n "title": "Top Hits Vol 7",\n "description": "Top hits vol 7. description",\n "releaseDate": "10-03-1987",\n "actors": [\n {\n "id": …Run Code Online (Sandbox Code Playgroud) 这个问题有点长,请耐心等待.在REST中,我认为我们不应该需要WADL或任何IDL.而是隐含地涵盖其概念的东西.我想到的方式是当我们(人)上网时,当我们第一次去网站时,我们不知道它提供了什么服务.你发现了html主页上的那些(或帮助部分中的站点地图页面)或者可能只是主页上的主菜单.如果你做一个类比,那么我们人类的主页或站点地图就是WSDL对WS-*或者WADL对REST服务的作用.只有它就像任何其他HTML内容一样.我认为在REST中,以下是一种做事的好方法,尊重HATEOS范式.拥有一个顶级(或默认)资源,列出指向其他资源的链接.对于一个库示例,请说RestLibrary.com/可能是这样的:
<root xmlns:lib="http://librarystandards.com/libraryml">
<resource class="lib:book">
<link type="application/vnd.libraryml+xml" template="mylib.com/book/{isbn}" />
<link type="application/vnd.libraryml+xml" rel="add" href="mylib.com/book" method="POST" />
<link type="application/vnd.libraryml+xml" rel="update" template="mylib.com/book/{isbn}" method="PUT" />
</resource>
<resource class="lib:bookList">
<link template="mylib.com/book?keywords={keywords}" type="application/vnd.openlibrary+xml" rel="search" />
</resource>
</root>
Run Code Online (Sandbox Code Playgroud)
请注意,假设媒体类型"application/vnd.libraryml + xml"是一个名为libraryml的已定义标准或(可能只是专有词汇表).此外,客户端应该能够理解这个"主页"资源(元素根,资源和链接).这是可以用来代替WADL的部分:一个任何客户都应该理解的抽象词汇表.例如,您可以使用像Atom这样的现有标准.但主要的想法是让任何客户都能理解抽象词汇.为什么不WADL呢?well wadl仅用于服务发现.这里的想法是拥有一个轻量级的抽象词汇表,作为超媒体的基础.一个"根"词汇.就像在owl中我们有owl:thing ...等现在如果客户端知道"libraryml" 标准它可以跟随它理解的东西的链接(在解析媒体类型属性和xmlns之后).如果没有,它就不会.
当我无法理解如何处理REST架构中的某些东西时,我倾向于看到我们的人类如何在Web中完成它.在Web中,我们使用HTML通用语言,使网站构建者能够提供任何特定内容,无论其对客户端(用户)的意义如何,浏览器都能理解HTML而不是其内容的"含义".用户理解(特定于域的)内容.如果我去说QuantumPhysics.org,我的浏览器可以渲染主页(毕竟它只是html),我可以阅读主页.如果我理解量子那么好我可以继续浏览.如果我不,我只是出去(除非我想学习硬道:))
那么这种方法有什么价值吗?难道我们不需要根超小词根,所以我们可以通过超媒体和"初始URI"概念获得成功吗?
编辑 是的为什么不将RDF作为根词汇!
我正在尝试构建一个RESTful Web服务,它应该为我的UI提供支持.如果我遵循纯粹的HATEOAS原则,我应该只展示集合中各个资源的URI.现在,假设我有一个父子关系,每个父母可以有50个孩子,并且UI需要在点击父母时显示所有孩子的部分数据.
如果我只使用父项公开子URI,则UI必须进行50次Web服务调用才能完成此操作.另一种方法是有一个单独的API,它将给出父节点以及有关子节点的部分信息,而不仅仅是URI.我确信这是一个很常见的问题.这里的平衡是什么?有哪些陷阱?从设计的角度来看,"唯一的URI"方法更简洁,但由于所有这些服务调用,它可能使UI非常慢并且在服务器上施加了大量负载.所以,另一种方法可能更实用.根据您的经验,哪个更好?
我正在试图弄清楚如何建立HAL链接templated: true.如果我使用
BasicLinkBuilder.linkToCurrentMapping().slash("api/public/blogs/{blog}").withRel("blog");
Run Code Online (Sandbox Code Playgroud)
在{和}字符仍然编码.知道如何使用Spring-hateo构建模板URL链接作为0.10.0.RELEASE其API吗?
谢谢.
我正在尝试找出在单页面应用程序中处理权限的正确方法,该应用程序直接与几个实现HATEOAS的RESTful API对话.
举个例子:
"作为我的应用程序的用户,我可以查看,启动和暂停作业,但不能阻止它们."
基础rest API具有以下资源:
/ jobs/{id}接受GET和PUT.GET返回作业模型,PUT接受作业模型作为请求体:
{
"_links" : {
"self" : "/jobs/12345678"
}
"id" : 12345678,
"description" : "foo job",
"state" : "STOPPED"
}
Run Code Online (Sandbox Code Playgroud)
接受的工作状态可以是:休眠| 跑步| 暂停了| 停止.
要求说在UI上我必须有按钮:
开始,暂停,停止
...并且仅基于登录用户的权限进行显示.
从API的角度来看,一切都可以作为服务器上的底层逻辑,确保用户在发出请求时无法将状态更新为STOPPED状态(可能会返回401).
通知app/UI用户权限的最佳方法是什么,因此它可以隐藏用户无权操作的任何按钮?
如果API提供权限列表,可能类似于:
{
"_links" : {
"self" : "/permissions",
"jobs" : "/jobs"
}
"permissions" : {
"job" : ["UPDATE", "DELETE"],
"job-updates" : ["START", "PAUSE"]
}
}
Run Code Online (Sandbox Code Playgroud)
或者应该更改API以便权限反映在HATEOS链接中可能类似于:
{
"_links" : {
"self" : "/jobs/12345678",
"start" : "/jobs/12345678/state?to=RUNNING",
"pause" : "/jobs/12345678/state?to=PAUSED",
}
"id" : 12345678,
"description" …Run Code Online (Sandbox Code Playgroud) 我们正在使用HATEOAS效果很好但是我们一直在关注性能并且从链接的构建中获得非常差的结果,即看起来像这样的代码
resource.add(linkTo(methodOn(SomeController.class).findAll())).withSelfRel());
Run Code Online (Sandbox Code Playgroud)
结果(我不关心尺寸,但无论如何都记录在下面)
Enabled links - ~438ms - 201 kb
Disable links - ~193ms - 84.6 kb
Run Code Online (Sandbox Code Playgroud)
大小是由于我们为每个资源分配了8个链接,所以我们预计大小,但不是速度减慢.
每回复一个对象大约2ms用于构建链接大约需要232ms(在此特定测试中有100个对象)
反正有加速吗?我们可以为toResources调用中的所有请求预先获取URI ,然后在中调用它toResource吗?
真正的RESTful API利用超媒体,以便客户端仅依靠服务器提供的动态超媒体来浏览应用程序(称为HATEOAS的概念)
这个概念很容易适用于Web应用程序,但是如何将它应用于单页应用程序,因为SPA通常在内部管理其状态(就导航而言,不依赖于服务器)?
我的感觉是SPA无法充分利用RESTful API或者我错过了什么?
谢谢
Riana
我有一个问题,这个问题一直在我脑海中流传.让我们假设我们在不同的层上使用后端和前端构建了我们的项目.所以,从前端,我们想得到一个客户,hal+json格式:
GET /customers/1 HTTP/1.1
Accept: application/hal+json
{
"name": "Alice",
"links": [ {
"rel": "self",
"href": "http://localhost:8080/customer/1"
} {
"rel": "transactions",
"href": "http://localhost:8080/customer/1/transactions"
}]
}
Run Code Online (Sandbox Code Playgroud)
然后,从前端我想获得所有客户交易.我的问题是:我该如何获得网址?它应该来自回应吗?或者我应该在内部构建它?
如果我们选择第一个选项,我认为在我们得到我们想要的那个之前,迭代所有链接可能并不优雅.此外,可能会出现我们没有我们想要的请求链接的情况.
如果我们使用第二个选项,如果我们实际上没有id,我不明白如何构建该URL.如果hateoas用链接替换id并且我还没有身体中的对象引用,我怎么能创建新的客户交易呢?
我认为也许服务应该同时支持 application/hal+json(面向用户)和application/json(面向客户端),但我不认为这是如何一般地完成它.
你怎么看?
默认情况下,当我们有一个暴露了save方法的存储库时,我们可以执行PATCH请求.然后,Spring Data REST从数据库中检索原始对象并将更改应用于实体,然后将其保存给我们(在JsonPatchHandler类中).这允许我们为类做以下请求
class Address {
Long id;
String street;
Long houseNumber;
}
Run Code Online (Sandbox Code Playgroud)
PATCH/api /地址/ 1与身体
{ houseNumber: 123 }
Run Code Online (Sandbox Code Playgroud)
只有这一个领域才会改变.
现在有了自定义控制器,我们希望在update方法中接收整个对象(在HATEOAS将其与来自DB的原始对象合并之后)
@RepositoryRestController
@ExposesResourceFor(Address.class)
@ResponseBody
@RequestMapping("/addresses")
public class AdddressController {
@PatchMapping("/{addressId}")
public Resource<Address> update(@RequestBody Resource<Address> addressResource, @PathVariable Long addressId) {
Address address= addressResource.getContent();
// .... some logic
address = addressRepository.save(address);
return new Resource<>(address);
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,在我会做一些逻辑的地方,我得到的地址是空字段而不是合并对象.
是否可以在Spring Data REST堆栈中插入自定义控制器,以便在修补请求时将它合并为我(就像它对普通存储库一样)?
编辑:我想找到一个透明地与PATCH(内容类型:application/json-patch + json)和PATCH(内容类型:application/hal + json)一起工作的解决方案
hateoas ×10
rest ×5
spring ×2
web-services ×2
.net ×1
angularjs ×1
api ×1
api-design ×1
asp.net ×1
asp.net-core ×1
c# ×1
hal-json ×1
hypermedia ×1
java ×1
json ×1
spring-mvc ×1
wadl ×1