Spring Data REST不完全指南(三)
上一篇我们介绍了使用Spring Data REST时的一些高级特性,以及使用代码演示了如何使用这些高级的特性。本文将继续讲解前面我们列出来的七个高级特性中的后四个。至此,这些特性能满足我们大部分的接口开发场景。
需要满足的一些要求:
1.针对字段级别,方法级别,类级别进行限制(禁止某些字段,方法,接口的对外映射)。
2.对数据增删改查的限制(禁止某些请求方法的访问)。
3.能个性化定义请求的路径。
4.对所传参数进行值校验。
5.响应统一处理。
6.异常处理。
7.数据处理的切面。
➡️本文,将演示7个要求中的其余四个要求。
对所传参数进行值校验
对于值校验,Spring 提供了Validator接口,Spring Data REST提供了使用Validator来进行值校验的功能。
首先我们通过实现Validator接口来创建一个校验器,然后在实现RepositoryRestConfigurer或Spring Data REST的RepositoryRestConfigurerAdapter的子类的配置中,重写configureValidatingRepositoryEventListener方法,并在ValidatingRepositoryEventListener上调用addValidator,传递要触发此校验器的事件和校验器的实例。以下示例显示了如何执行此操作:
public class SaveTenantValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Tenant.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Tenant tenant = (Tenant) target;
if (StringUtils.isEmpty(tenant.getMobile())) {
errors.rejectValue("mobile", "1001", "手机号不能为空");
}
}
}
如上,我们声明了一个Validator类,作为对手机号校验的Validator。接着我们通过以下代码注册我们的校验器。
@Component
public class SpringDataRestCustomization implements RepositoryRestConfigurer {
@Override
public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", new SaveTenantValidator());
}
}
validatingListener.addValidator("beforeCreate", new SaveTenantValidator());
我们使用validatingListener.addValidator()
来注册我们的校验器。该方法传入两个参数,第一个代表着要校验的事件,"beforeCreate"即代表着在插入新纪录之前,对插入数据进行校验。spring Data REST还提供了其他的事件:
BeforeCreateEvent
AfterCreateEvent
BeforeSaveEvent
AfterSaveEvent
BeforeLinkSaveEvent
AfterLinkSaveEvent
BeforeDeleteEvent
AfterDeleteEvent
我们都可以从字面意思进行理解。
方法中的第二个参数,就是指定我们要注册的校验器,如上代码中,我们对我们刚刚创建的校验器进行注册。
如下为验证效果:
响应统一处理
有时候我们需要对响应结果进行统一处理,比如,我们希望我们的响应结果中包含当前时间的时间戳又或者我们希望我们的HAL格式的响应数据中增加其他的链接。这时候,我们可以通过响应统一处理来完成这种看似重复性的工作。但是Spring Data REST并没有提供现成的功能,不过我们可以通过覆盖Spring Data REST响应处理程序,来实现这一目标。
@RepositoryRestController
public class TenantController {
private final TenantRepository tenantRepository;
@Resource
private RepositoryEntityLinks entityLinks;
@Autowired
public TenantController(TenantRepository tenantRepository) {
this.tenantRepository = tenantRepository;
}
@GetMapping(value = "/tenantPath/search/mobile")
public ResponseEntity<?> getByMobile(@RequestParam String mobile) {
Tenant tenant = tenantRepository.findFirstByMobile(mobile);
EntityModel<Tenant> resource = new EntityModel<>(tenant);
resource.add(linkTo(methodOn(TenantController.class).getByMobile(mobile)).withSelfRel());
resource.add(entityLinks.linkToSearchResource(Tenant.class, LinkRelation.of("findAllByIdCardContaining")));
return ResponseEntity.ok(resource);
}
}
如上代码,我们使用了@RepositoryRestController
注解来创建了一个控制器,并定义了一个路径的请求,以此我们覆盖了之前Spring Data REST自动为我们提供的相同路径的接口。我们给接口的响应增加了两个链接。
注意:上述代码中用到了Spring HATEOAS的库,所以我们需要增加Spring HATEOAS的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.atteo</groupId>
<artifactId>evo-inflector</artifactId>
</dependency>
现在我们访问http://localhost:8080/tenantPath/search/mobile?mobile=186****3331
,看到响应结果:
{
"name": "王一",
"mobile": "186****3331",
"rentDateTime": "2020-04-22 17:48:40",
"_links": {
"self": {
"href": "http://localhost:8080/tenantPath/search/mobile?mobile=186****3331"
},
"findAllByIdCardContaining": {
"href": "http://localhost:8080/tenantPath/search/findAllByIdCardContaining{?idCard,page,size,sort,projection}",
"templated": true
}
}
}
可以看到,links属性中链接已经变成我们指定的链接了。
异常统一处理
Spring Data REST中并没有提供异常处理的功能,但是我们可以使用Springboot中自带的异常处理功能来实现我们的要求。
@Slf4j
@ControllerAdvice
public class ExceptionTranslator {
@ExceptionHandler
public ResponseEntity<Object> handleEmailAlreadyUsedException(NullPointerException ex, NativeWebRequest request) {
log.info("遇到空指针");
return ResponseEntity.ok(List.of("拦截到空指针异常"));
}
}
如上,我们声明了一个异常处理器。接下来我人为制造一个错误。
@RepositoryRestController
public class TenantController {
private final TenantRepository tenantRepository;
@Autowired
public TenantController(TenantRepository tenantRepository) {
this.tenantRepository = tenantRepository;
}
@GetMapping(value = "/tenantPath/search/mobile")
public ResponseEntity<?> getByMobile(@RequestParam String mobile) {
if (1 == 1) {
throw new NullPointerException();
}
Tenant tenant = tenantRepository.findFirstByMobile(mobile);
EntityModel<Tenant> resource = new EntityModel<>(tenant);
resource.add(linkTo(methodOn(TenantController.class).getByMobile(mobile)).withSelfRel());
return ResponseEntity.ok(resource);
}
}
此时,我们请求此接口:
[
"拦截到空指针异常"
]
可以看到,我们的异常被我们的异常处理器拦截掉了。
数据切面处理
Spring Data REST提供了类似的Aop切面操作,虽然不能和Spring的原生aop相比,但是其简洁性也能满足需求。Spring Data REST提供的是基于事件的切面。如下我们声明了一个切面。
@Component
@Slf4j
@RepositoryEventHandler
public class TenantEventHandler {
@HandleBeforeDelete
protected void onBeforeDelete(Tenant entity) {
log.info("现在要开始删除操作了,删除对象:{}", entity);
}
@HandleAfterDelete
protected void onAfterDelete(Tenant entity) {
log.info("删除对象完成,删除对象:{}", entity);
}
}
如上,我们声明了一个切面,我们可以在删除操作之前和之后进行额外的逻辑处理,示例中很简单,我们使用日志记录事件的发生。
此时,我们访问项目的删除接口curl --location --request DELETE 'http://localhost:8080/tenantPath/1'
我们可以看到控制输出了相应的日志:
2020-04-23 17:26:29.950 INFO 38077 --- [nio-8080-exec-1] c.e.d.configuration.TenantEventHandler : 现在要开始删除操作了,删除对象:Tenant(id=1, name=王一, idCard=3305221, mobile=1863331, rentDateTime=2020-04-22T17:24:46.105897, house=House(id=2, houseNumber=1101, owner=张三, idCard=3305211))
2020-04-23 17:26:30.035 INFO 38077 --- [nio-8080-exec-1] c.e.d.configuration.TenantEventHandler : 删除对象完成,删除对象:Tenant(id=1, name=王一, idCard=3305221, mobile=1863331, rentDateTime=2020-04-22T17:24:46.105897, house=House(id=2, houseNumber=1101, owner=张三, idCard=3305211))
此时,我们的数据切面处理生效了,除此之外,Spring Data REST还提供了如下几个基于事件的切面:
总结
至此,我们先前列出的所有功能特性三篇文章中都有涉及到,通过引入这些功能特性,我们能更加轻松的使用Spring Data REST,并且也能满足我们大部分接口开发的场景。当然三篇文章不能涉及Spring Data REST的全部,有兴趣的小伙伴可以访问Spring Data REST的官方文档查看更多关于Spring Data REST的特性及信息。
本系列文章演示代码地址:https://gitee.com/jeker8chen/spring-data-rest-in-practice.git
关注笔者公众号,推送各类原创/优质技术文章 ⬇️
Spring Data REST不完全指南(三)的更多相关文章
- Spring Data REST不完全指南(二)
上一篇文章介绍了Spring Data REST的功能及特征,以及演示了如何在项目中引入Spring Data REST并简单地启动演示了Spring Data REST项目.在本文中,我们将深入了解 ...
- Spring Data REST不完全指南(一)
简介 Spring Data REST是Spring Data项目的一部分,可轻松在Spring Data存储库上构建超媒体驱动的REST Web服务. Spring Data REST 构建在 Sp ...
- ElasticSearch(十三):Spring Data ElasticSearch 的使用(三)——NativeSearchQuery 高亮查询
在Elasticsearch的实际应用中,经常需要将匹配到的结果字符进行高亮显示,此处采取NativeSearchQuery原生查询的方法,实现查询结果的高亮显示. /** * 高亮查询 */ @Te ...
- Spring Data JPA教程, 第二部分: CRUD(翻译)
我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用.该应用要求如下: pe ...
- spring mvc Spring Data Redis RedisTemplate [转]
http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...
- Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解
一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...
- Spring Data Elasticsearch 用户指南
https://www.jianshu.com/p/27e1d583aafb 翻译自官方文档英文版,有删减. BioMed Central Development Team version 2.1.3 ...
- Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)
在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...
- spring-boot (三) spring data jpa
学习文章来自:http://www.ityouknow.com/spring-boot.html spring data jpa介绍 首先了解JPA是什么? JPA(Java Persistence ...
随机推荐
- 不同label样本画图——颜色分配plt.cm.Spectral
不同label样本画图——颜色分配plt.cm.Spectralhttps://blog.csdn.net/wang_zuel/article/details/102940092 关于plt.cm.S ...
- iphone ios app互相调用
正好要做这个,记录一下 1.ios应用程序间互相启动 2.网页如何调用自己的app http://www.dotblogs.com.tw/yang5664/archive/2012/11/24/850 ...
- Hadoop 集群的基准测试
集群搭建完毕之后,需要对集群进行基准测试 第一方面:基本测试 服务启动,是否可用,简单的应用 *hdfs 读写操作 bin/hdfs dfs -mkdir - p /user/chris/tmp/co ...
- IEnumerable和IQueryable在使用时的区别
最近在调研数据库查询时因使用IEnumerable进行Linq to entity的操作,造成数据库访问缓慢.此文讲述的便是IEnumerable和IQueryable的区别. 微软对IEnumera ...
- 9.Metasploit制作木马后门
01木马与后门 木马?后门? 木马和后门都有害,尤其是木马,它由攻击者主动发起,稍不留心就会被利用:后门原来是留给自己方便用的,但也有可能被非法利用,这两种程序都会给用户带来损失. 木马是指潜伏在 ...
- 老技术新谈,Java应用监控利器JMX(1)
先聊聊最近比较流行的梗,来一次灵魂八问. 配钥匙师傅: 你配吗? 食堂阿姨: 你要饭吗? 算命先生: 你算什么东西? 快递小哥: 你是什么东西? 上海垃圾分拣阿姨: 你是什么垃圾? 滴滴司机: 你搞清 ...
- 《Three.js 入门指南》2- 照相机
2.1 什么是照相机 我们使用Three.js创建的场景是三维的,而通常情况下显示屏是二维的,那么三维的场景如何显示到二维的显示屏上呢?照相机就是这样一个抽象,它定义了三维空间到二维屏幕的投影方式,用 ...
- Linux 定时实行一次任务命令
当我们想在指定的时间自动执行 一次 任务的时候,可以使用at命令 启动服务 使用时首先检查atq的服务是否启动 service atd status # 检查atd的状态 service atd st ...
- Java进行二元操作类型转换
当对两个数值进行二元操作时,先要将两个操作数转换为同一种类型,然后再进行计算. 如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型. 否则,如果其中一个操作数是float ...
- HDU1087:Super Jumping! Jumping! Jumping!(DP水题)
Super Jumping! Jumping! Jumping! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 ...