不要讨厌HATEOAS Part Deux:HATEOAS的春天

在我关于HATEOAS的系列文章的最后结论中,我们将深入探讨如何使用Spring-Data-REST和Spring-HATEOAS实现HATEOAS。 HATEOAS的春天到了!

我整理了一个有效的项目,该项目将演示下面的代码示例以及其他一些功能。 该项目可以在这里找到: https : //github.com/in-the-keyhole/hateoas-demo-II 。 需要JDK 8和Maven,否则不需要外部依赖项即可运行项目。

服务资源

通过其资源与Web服务交互是REST的核心设计约束之一。 使用Spring-Data和Spring-MVC ,开始提供资源并不是很困难。 您需要为要提供服务的实体添加一个Repository ,并实现一个控制器来为其提供服务。 但是, Spring-Data-REST使此过程变得更加容易,并在此过程中提供了更丰富的资源(即添加超媒体标记)。

@RepositoryRestResource
public interface ItemRepo extends CrudRepository<Item, Long> {
}

就这么简单。 如果您启动您的弹簧启动应用程序并导航到http://localhost:8080/items (并已做了一些其他必要的配置 为 好 ),你应该得到的回报JSON看起来是这样的:

{"_embedded" : {"items" : [ {"name" : "Independence Day","description" : "Best. Movie. Speech. Ever!","price" : 10.0,"type" : "Movies","_links" : {"self" : {"href" : "http://localhost:8080/api/items/21"},"item" : {"href" : "http://localhost:8080/api/items/21"}}},...]},"_links" : {"self" : {"href" : "http://localhost:8080/items/"},"profile" : {"href" : "http://localhost:8080/profile/items"},"search" : {"href" : "http://localhost:8080/items/search"}}
}

除了易于演示的GET功能外,Spring-Data-REST还增加了PUT (出于某种原因决定将PUT用于创建和更新的原因,Spring-Data-REST)的功能,并DELETE资源以及检索资源的ID。 仅两行代码就具有很多功能!

分页和排序

资源通常会有很多记录。 通常,由于各个级别的资源成本很高,因此您不希望应要求返回所有这些记录。 分页是解决此问题的常用解决方案,而Spring-Data-REST使其易于实现。

另一个常见的需求是允许客户端对来自资源的返回进行排序的能力,在这里Spring-Data-REST还是可以解决的。 为了使用Item资源实现此功能,我们需要从将CrudRepository扩展为CrudRepositoryPagingAndSortingRepository所示:

@RepositoryRestResource
public interface ItemRepo extends PagingAndSortingRepository<Item, Long> {
}

重新启动应用程序并返回http://localhost:8080/items ,返回的内容最初看起来是相同的,但是在页面底部附近,我们看到了一些新的JSON对象:

{...    "_links" : {"first" : {"href" : "http://localhost:8080/items?page=0&size=20"},"self" : {"href" : "http://localhost:8080/items"},"next" : {"href" : "http://localhost:8080/items?page=1&size=20"},"last" : {"href" : "http://localhost:8080/items?page=1&size=20"},"profile" : {"href" : "http://localhost:8080/profile/items"},"search" : {"href" : "http://localhost:8080/items/search"}},"page" : {"size" : 20,"totalElements" : 23,"totalPages" : 2,"number" : 0}
}

Spring-Data-REST提供了用于浏览资源返回页面的超媒体控件。 last,next,prev和first(如果适用)(注意:Spring-Data-REST使用基于0的数组进行分页)。 如果仔细观察,您还将注意到Spring-Data-REST如何允许客户端操纵每页的返回数( .../items?size=x )。 最后,还添加了排序,并可以使用URL参数: .../items?sort=name&name.dir=desc

搜索资源

因此,我们正在提供资源,对退货进行分页,并允许客户对这些退货进行排序。 这些都是非常有用的,但是客户端通常会希望搜索资源的特定子集。 这是Spring-Data-REST使极其简单的另一项任务。

@RepositoryRestResource
public interface ItemRepo extends PagingAndSortingRepository<Item, Long> {List<Item> findByType(@Param("type") String type);@RestResource(path = "byMaxPrice")@Query("SELECT i FROM Item i WHERE i.price <= :maxPrice")List<Item> findItemsLessThan(@Param("maxPrice") double maxPrice);@RestResource(path = "byMaxPriceAndType")@Query("SELECT i FROM Item i WHERE i.price <= :maxPrice AND i.type = :type")List<Item> findItemsLessThanAndType(@Param("maxPrice") double maxPrice, @Param("type") String type);
}

上面是一些用户可能希望通过以下方式搜索商品的查询:商品的类型,商品的最高价格,然后将这两个参数组合在一起。 导航到http://localhost:8080/items/search ,Spring-Data-REST呈现所有可用的搜索选项以及如何与它们交互。 与搜索端点交互时,也会启用根资源端点处可用的分页和排序功能!

..."findItemsLessThan" : {"href" : "http://localhost:8080/items/search/byMaxPrice{?maxPrice}","templated" : true},"findByType" : {"href" : "http://localhost:8080/items/search/findByType{?type}","templated" : true},"findItemsLessThanAndType" : {"href" : "http://localhost:8080/items/search/byMaxPriceAndType{?maxPrice,type}","templated" : true},
...

改变资源的形状

有时候改变端点服务的实体的形状是有益的。 您可能需要展平对象树,隐藏字段或更改字段名称以维护合同。 Spring-Data-REST提供了使用投影来操纵形状的功能。

首先,我们需要创建一个接口并使用@Projection对其进行注释:

@Projection(name = "itemSummary", types = { Item.class })
public interface ItemSummary {String getName();String getPrice();
}

这将允许Spring-Data-REST根据请求以ItemSummary形状提供我们的Item实体: http://localhost:8080/api/items/1?projection=itemSummary 。 如果我们想使ItemSummary默认的形状,我们击中时返回/items端点可以通过添加来完成excerptProjectio n向@RepositoryRestResource上标注ItemRepo

@RepositoryRestResource(excerptProjection = ItemSummary.class)
public interface ItemRepo extends PagingAndSortingRepository<Item, Long> {

现在,当我们点击../items ,我们的回报看起来像这样:

...
{"name" : "Sony 55 TV","price" : "1350.0","_links" : {"self" : {"href" : "http://localhost:8080/api/items/2"},"item" : {"href" : "http://localhost:8080/api/items/2{?projection}","templated" : true}}
}
...

自定义资源的端点

实体的名称可能不一定总是作为资源端点的名称。 它可能不符合遗留需求,您可能需要在资源的终结点之前加上前缀,或者只是想要一个不同的名称。 Spring-Data-REST提供了满足所有这些需求的钩子。

更改资源名称:

@RepositoryRestResource(collectionResourceRel = "merchandise", path = "merchandise")
public interface ItemRepo extends PagingAndSortingRepository<Item, Long> {
}

并添加基本路径:

@Configuration
public class RestConfiguration extends RepositoryRestConfigurerAdapter {@Overridepublic void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {config.setBasePath("api");}}

现在,将不再是通过../api/merchandise来提供Item实体,而是通过../items来提供../api/merchandise

保护资源

安全是一个非常重要和复杂的主题。 即使是整个谈话也几乎没有触及表面。 因此,将此部分视为对这个主题的轻微磨损。

隐藏场

如上一节所述,投影是隐藏字段的一种方法。 另一种更安全的方法是在如下所示的字段上使用@JsonIgnore以防止其返回:

public class Item implements Serializable, Identifiable<Long> {@JsonIgnore@Column(name = "secret_field")private String secretField;
}

限制通过HTTP的访问

在某些情况下,无论您是谁,都根本无法通过HTTP访问功能。 这可以通过@RestResource(exported = false)来实现,它告诉Spring-Data-REST完全不将该资源或资源的一部分发布到Web上。 可以在“类型”和“方法”级别上进行设置。 如果要广泛拒绝,然后显式定义应访问的内容,则在方法级别也可以覆盖类型级别。

方法级别:

public interface OrderRepo extends CrudRepository<Order, Long> {@Override@RestResource(exported = false)<S extends Order> S save(S entity);
}

类型级别,具有方法级别覆盖:

@RestResource(exported = false)
public interface OrderRepo extends CrudRepository<Order, Long> {@Override@RestResource(exported = true)<S extends Order> S save(S entity);
}

另一种方法(如果您愿意)是扩展Repository接口,而仅定义您希望客户端可以访问的方法。

public interface PaymentRepo extends Repository<Payment, Long> {Payment findOne(Long id);<S extends Payment> S save(S entity);
}

通过角色限制访问

您可能还希望将功能限制为仅某些类型的用户。

@RepositoryRestResource(collectionResourceRel = "merchandise", path = "merchandise")
public interface ItemRepo extends PagingAndSortingRepository<Item, Long> {@PreAuthorize("hasRole('ADMIN')")<S extends Item> S save(S entity);@PreAuthorize("hasRole('ADMIN')")<S extends Item> Iterable<S> save(Iterable<S> entities);
}

虽然我认为并不是严格要求的,但是由于可能与Spring-MVC过滤器进行了一些时髦的交互,因此需要一些其他的URL配置才能使基于角色的安全性发挥作用。 (我花了很多时间研究这个问题。)但是,无论如何,实现多层安全性通常是一个好习惯,因此,这也不一定是错误的:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {@Override@Autowiredpublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN")//.and().withUser("user").password("password").roles("USER");}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().antMatcher("/merchandise").authorizeRequests().antMatchers(HttpMethod.POST).hasAnyRole("ADMIN")//.and().antMatcher("/merchandise").authorizeRequests().antMatchers(HttpMethod.PUT).hasAnyRole("ADMIN")//.and().antMatcher("/**").authorizeRequests().antMatchers(HttpMethod.DELETE).denyAll()//.and().antMatcher("/merchandise").authorizeRequests().antMatchers(HttpMethod.GET).permitAll()//.and().antMatcher("/**").authorizeRequests().anyRequest().authenticated().and().httpBasic();}
}

@RestResource一样, @PreAuthorize也可以放在类型级别,并在方法级别覆盖。

@PreAuthorize("hasRole('USER')")
public interface OrderRepo extends CrudRepository<Order, Long> {
}

Spring-HATEOAS的附加定制

到目前为止,我已经演示了Spring-Data-REST的所有功能,以及如何使HATEOAS服务的实现变得轻而易举。 las,使用Spring-Data-REST可以做的事情有局限性。 幸运的是,还有另一个Spring项目Spring-HATEOAS可以从那里开始。

Spring-HATEOAS简化了向资源添加超媒体标记的过程,对于处理资源之间的自定义交互非常有用。 例如,将商品添加到订单中:

@RequestMapping("/{id}")
public ResponseEntity<Resource<Item>> viewItem(@PathVariable String id) {Item item = itemRepo.findOne(Long.valueOf(id));Resource<Item> resource = new Resource<Item>(item);if (hasExistingOrder()) {// Provide a link to an existing Orderresource.add(entityLinks.linkToSingleResource(retrieveExistingOrder()).withRel("addToCart"));} else {// Provide a link to create a new Orderresource.add(entityLinks.linkFor(Order.class).withRel("addToCart"));}resource.add(entityLinks.linkToSingleResource(item).withSelfRel());return ResponseEntity.ok(resource);
}

这样,我们就覆盖了Spring-Data-REST提供的默认/merchandise/(id)功能,现在将返回以下结果:

{"name" : "Samsung 55 TV","description" : "Samsung 55 LCD HD TV","price" : 1500.0,"type" : "Electronics","_links" : {"addToCart" : {"href" : "http://localhost:8080/api/orders"},"self" : {"href" : "http://localhost:8080/api/merchandise/1{?projection}","templated" : true}}
}

因此,我们的客户代码现在可以呈现一个链接,使用户可以轻松地将商品添加到他们的购物车或创建新的购物车并向其中添加商品。

结论

HATEOAS是REST规范中经常被忽略的部分,主要是因为实现和维护它会非常耗时。 Spring-Data-REST和Spring-HATEOAS大大减少了实现时间和维护时间,这使得HATEOAS在RESTful服务中实现更加实用。

我只能接触到Spring-Data-REST和Spring-HATEOAS必须提供的某些功能。 有关它们各自功能集的完整说明,建议您查看下面链接的参考文档。 如果您有任何疑问或需要进一步说明,请随时在下面的评论部分中提问。

其他资源

  • http://docs.spring.io/spring-data/rest/docs/2.5.1.RELEASE/reference/html/
  • http://docs.spring.io/spring-hateoas/docs/0.19.0.RELEASE/reference/html/

翻译自: https://www.javacodegeeks.com/2016/05/dont-hate-hateoas-part-deux-springtime-hateoas.html

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

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

相关文章

linq内联左联

内联&#xff1a;没有into 左联&#xff1a;有into 例子&#xff1a; from GoodsStore in this.GetCurrentDbSession.Tbl_OfficeSupplies_GoodsStoreDLL.LoadEntities(a > (Guid.EmptyGoodsID?true:a.GoodsIDGoodsID)) join goods in GetCurrentDbSession.Tbl_OfficeSuppli…

计算机操作员实操高级试题,计算机操作员高级实操(以往考过,可做平时练习素材)答题.doc...

试题1、计算机安装、连接、调试试题2、文字录入b)中文基本录入&#xff1a;在十分钟之内录入以下中文内容&#xff0c;错误率不高于千分之三。c)公式录入&#xff1a;在文档的结尾处录入下列公式。d)完成以上操作后&#xff0c;将最终结果以“高级2-1.doc”为文件名&#xff0c…

ls 显示目录下的内容和文件相关属性信息

1.命令功能 ls命令是“list directory contents”&#xff0c;显示当前目录下的内容和文件属性。 2.语法格式 ls [option] file ls 选项 文件名 3.选项说明 参数 参数说明 -a 显示全部文件包括隐藏文件&#xff0c;包括.和.. -A 显示全部文件&#xff0c;但是不包括.和…

计算机休眠下睡眠的不同点是什么,电脑的关机选项里,休眠和睡眠有什么具体的区别呢?...

电脑的关机选项里&#xff0c;休眠和睡眠有什么具体的区别呢&#xff1f;以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;电脑的关机选项里&#xff0c;休眠和睡眠有什么具体的区别呢&#x…

maven jpa_使用Hibernate 4,JPA和Maven的架构创建脚本

maven jpa这种情况很简单–您想要在构建应用程序时生成数据库模式创建脚本&#xff08;然后在目标数据库上执行脚本&#xff09;&#xff0c;这在Hibernate 3中相对容易&#xff0c;因为有 hibernate3-maven-plugin &#xff0c;但是与Hibernate 4不兼容。当然&#xff0c;对于…

读取带空格字符串小结

1 &#xff0c;gets() 可以无限读取&#xff0c;以回车结束读取&#xff0c;C语言中的函数&#xff0c;在C中运行会产生bug。 如&#xff1a; #include <iostream> #include <cstdio> using namespace std; int main() { chara[50]; cin>>a; gets(a); cout&l…

计算机x线摄影的发展趋势,计算机X线摄影技术----CR 新进展

结构化存贮荧光体(针状成像板)(Structured Storage Phosphors(Needle ImagePlates))结构化荧光体&#xff0c;也就是各向异性物理结构&#xff0c;已经存在很久&#xff0c;且已得到广泛应用&#xff0c;比如在影像增强管中吸收X线&#xff0c;并将射线激励的可见光导入成像链的…

开发一个智能问答机器人(优化篇)

上一篇介绍了整个问答机器人的技术架构和特定&#xff0c;本篇着重说下 如何让机器人&#xff08;看起来&#xff09;更智能 输入联想 使用jquery.autosuggest.js实现的输入联想&#xff0c;在输入2个字后&#xff0c;在5000个问答中基于全文检索&#xff0c;检索10条记录&…

使用Java将项目插入DynamoDB表

在上一篇文章中&#xff0c;我们学习了如何使用Java创建DynamoDB表。 下一步是将项目插入到先前创建的DynamoDB表中。 请记住&#xff0c;对于插入操作&#xff0c;最基本的步骤是指定主键。 对于表用户&#xff0c;主键是属性电子邮件。 您可以根据需要添加任意数量的属性&am…

计算机vb操作题评分细则,上机考试的试题及评分标准.doc

上机考试的试题及评分标准上机题总分占40分&#xff0c;其中改错题占14分&#xff0c;编程题占26分。(1)改错题&#xff1a;题目中都是设3个错误点(在历年上机考题中也出现过只有2个错误点的试题)&#xff0c;一般分别是语法错误(如数组的声明、重复定义等略有难度的语法错误)、…

面试汇总

HTML部分 1.HTML5新特性&#xff0c;语义化 可以参考 https://blog.csdn.net/qq_26562641/article/details/54669288 2.浏览器的标准模式和怪异模式 可以参考 http://www.cnblogs.com/zzgyq/p/8630709.html 3. xhtml和html的区别 XHTML 元素必须被正确地嵌套…

64 合并排序数组

原题网址&#xff1a;http://www.lintcode.com/zh-cn/problem/merge-sorted-array/# 合并两个排序的整数数组A和B变成一个新的数组。 注意事项 你可以假设A具有足够的空间&#xff08;A数组的大小大于或等于mn&#xff09;去添加B中的元素。 您在真实的面试中是否遇到过这个题&…

Ios9 html5,ios9,html5_ios9下在浏览器中通过scheme打开app的问题,ios9,html5 - phpStudy

ios9下在浏览器中通过scheme打开app的问题ios9系统下&#xff0c;safari下通过iframe(scheme)的方式跳app&#xff0c;无法打开app&#xff0c;通过location.hrefscheme的方式倒是可以&#xff0c;不过在没有安装app时&#xff0c;这种方式可能会直接跳转到一个错误页面(无法打…

使用Spring Boot进行面向方面的编程

在上一篇文章中&#xff0c;我提供了一个有关如何通过使用ProxyFactoryBean并实现MethodBeforeAdvice接口在Spring实现宽高比定向的简单示例。 在此示例中&#xff0c;我们将学习如何通过使用Spring Boot和Aspect4j注释来实现方面方向。 让我们从gradle文件开始。 group com…

java中的单例模型

参考网址:http://www.runoob.com/design-pattern/singleton-pattern.html 1.目的:保证一个类仅有一个实例&#xff0c;并提供一个访问它的全局访问点。(比如世界只有一个月亮,党只有一个主席) 2. 优点&#xff1a; 1、在内存里只有一个实例&#xff0c;减少了内存的开销&#x…

计算机电路基础张志良,计算机电路基础

图书简介配套资源&#xff1a;电子课件本书特色&#xff1a;★ 金牌作者编写&#xff0c;专门针对计算机专业设计教学内容★ 内容广、难度浅、适用面宽★ 配有《学习指导与习题解答》(ISBN 978-7- 111- 35112-2)本书配套资源&#xff0c;样书均可在本页下载申请&#xff0c;也可…

Java-变量函数 上

类的组成&#xff08;三部分&#xff09;全局变量&#xff08;成员变量&#xff09;和局部变量成员方法&#xff08;函数&#xff09;变量按照变量的数据类型分类基本数据类型 字符型 布尔 整型 浮点型引用数据类型 String 数组根据变量定义的位置不同&#xff08;或者…

计算机程序备份,将应用程序快照备份到计算机

Linux/UNIX 示例脚本创建一个 shell (.sh) 文件&#xff0c;在其中包含类似如下所示的脚本&#xff0c;以自动下载快照。如果密码中包含特殊字符&#xff0c;请参阅“处理特殊字符”。#!/bin/sh# Sample script to download and maintain 10 maintenance backups# Update the f…

跨站点脚本(xss)_跨站点脚本(XSS)和预防

跨站点脚本(xss)如OWASP网站&#xff08;https://www.owasp.org/index.php/Cross-site_Scripting_(XSS&#xff09;&#xff09;所述&#xff0c;跨站点脚本&#xff08;XSS&#xff09;攻击的变种几乎是无限的。 在这里&#xff0c;我建议使用基于Servlet筛选器的解决方案来清…

ajax的请求参数详解以及前后台交互详解

function rejectSub(){//从隐藏域中拿到userIDvar userId $("input:hidden[nameuserId]").val();var flag;$.ajax({type : "POST",//请求方式有post&#xff0c;get请求方式&#xff0c;这里是post请求url:${base}/compactedExpert/qcVerificationCompact…