上一篇我们介绍了使用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不完全指南(三)的更多相关文章

  1. Spring Data REST不完全指南(二)

    上一篇文章介绍了Spring Data REST的功能及特征,以及演示了如何在项目中引入Spring Data REST并简单地启动演示了Spring Data REST项目.在本文中,我们将深入了解 ...

  2. Spring Data REST不完全指南(一)

    简介 Spring Data REST是Spring Data项目的一部分,可轻松在Spring Data存储库上构建超媒体驱动的REST Web服务. Spring Data REST 构建在 Sp ...

  3. ElasticSearch(十三):Spring Data ElasticSearch 的使用(三)——NativeSearchQuery 高亮查询

    在Elasticsearch的实际应用中,经常需要将匹配到的结果字符进行高亮显示,此处采取NativeSearchQuery原生查询的方法,实现查询结果的高亮显示. /** * 高亮查询 */ @Te ...

  4. Spring Data JPA教程, 第二部分: CRUD(翻译)

    我的Spring Data Jpa教程的第一部分描述了,如何配置Spring Data JPA,本博文进一步描述怎样使用Spring Data JPA创建一个简单的CRUD应用.该应用要求如下: pe ...

  5. spring mvc Spring Data Redis RedisTemplate [转]

    http://maven.springframework.org/release/org/springframework/data/spring-data-redis/(spring-data包下载) ...

  6. Spring Data Redis简介以及项目Demo,RedisTemplate和 Serializer详解

    一.概念简介: Redis: Redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写,详细的信息在Redis官网上面有,因为我自己通过google等各种渠道去学习Redis, ...

  7. Spring Data Elasticsearch 用户指南

    https://www.jianshu.com/p/27e1d583aafb 翻译自官方文档英文版,有删减. BioMed Central Development Team version 2.1.3 ...

  8. Spring Data JPA教程, 第三部分: Custom Queries with Query Methods(翻译)

    在本人的Spring Data JPA教程的第二部分描述了如何用Spring Data JPA创建一个简单的CRUD应用,本博文将描述如何在Spring Data JPA中使用query方法创建自定义 ...

  9. spring-boot (三) spring data jpa

    学习文章来自:http://www.ityouknow.com/spring-boot.html spring data jpa介绍 首先了解JPA是什么? JPA(Java Persistence ...

随机推荐

  1. HDU - 1005 Number Sequence 矩阵快速幂

    HDU - 1005 Number Sequence Problem Description A number sequence is defined as follows:f(1) = 1, f(2 ...

  2. AC自动机(初步学习)

    一开始讲AC自动机就是在字典树上做一个KMP,吓得我感觉好难,不过了解了以后,感觉也就是有点难度,不吓人. 它只是在字典树上用了KMP的思想 典型问题:给n个模式串和一个文本串,问有多少个模式串在文本 ...

  3. POJ - 1276 二进制优化多重背包为01背包

    题意:直接说数据,735是目标值,然后3是后面有三种钱币,四张125的,六张五块的和三张350的. 思路:能够轻易的看出这是一个多重背包问题,735是背包的容量,那些钱币是物品,而且有一定的数量,是多 ...

  4. 从零搭建一个SpringCloud项目之Feign搭建

    从零搭建一个SpringCloud项目之Feign搭建 工程简述 目的:实现trade服务通过feign调用user服务的功能.因为trade服务会用到user里的一些类和接口,所以抽出了其他服务需要 ...

  5. elasticsearch报Fielddata is disabled on text fields by default

    我刚玩elk没几天,今天启动kibana之后执行查询看见elasticsearch报了一个错误 Caused by: java.lang.IllegalArgumentException: Field ...

  6. RedHat 6.7 使用 CentOS 6 的源

    1. 移除 RedHat 相关软件包 # yum -y remove subscription-manager and rh-check # rpm -aq | grep yum | xargs rp ...

  7. 一分钟 Get 时序数据库 InfluxDB 的技能

    1. 通过上期分享<实践指路明灯,源码剖析flink-metrics>,对当下较火的流式处理框架 flink 的指标监控体系有了全局的认识,并结合 flink-metrics-xxxx 模 ...

  8. 实际开发中 dao、entity的代码怎样自动生成?一款工具送给你

    01 关注"一猿小讲"朋友,都知道以往的文章一直倡导拒绝 CRUD,那到底什么是 CRUD?今天咱们就聊聊 Java 妹子小猿与数据库老头交互的事儿. 产品小汪铿锵有力的说:小猿同 ...

  9. Light of future-冲刺Day 5

    目录 1.SCRUM部分: 每个成员进度 SCRUM 会议的照片 签入记录 代码运行截图 用户浏览界面 订单详情界面 管理员浏览界面 新增后台界面 2.PM 报告: 时间表 燃尽图 任务总量变化曲线 ...

  10. Java Object类学习总结

    这篇博文发出来总有点问题,转为图片了,谢谢看官支持.