自动生成RESTful API——Spring Data Rest

news/2025/1/6 5:33:52/

一、Spring Data Rest

Spring Data REST 是 Spring Data 项目的一部分,它提供了一种快速创建 RESTful Web 服务的方法,可以直接暴露 Spring Data 仓库中的数据。通过 Spring Data REST,开发者可以轻松地将数据持久层的操作(如 CRUD 操作)暴露为 RESTful API,而不需要编写大量的控制器代码。这大大简化了 RESTful API 的开发过程。

1、主要特点

自动暴露 Repository 为 RESTful API:
○ Spring Data REST 可以自动将 Spring Data 仓库中的方法暴露为 RESTful API。例如,如果你有一个 UserRepository,Spring Data REST 会自动为你生成相应的 CRUD 操作的 RESTful 端点。

HATEOAS 支持:

○ HATEOAS(Hypermedia As The Engine Of Application State)是 REST 架构风格的一个重要原则。Spring Data REST 生成的 API 自动包含超媒体链接,帮助客户端发现和导航资源。

自定义控制器:

○ 虽然 Spring Data REST 可以自动暴露仓库方法,但你仍然可以添加自定义控制器来处理特定的业务逻辑。你可以通过 @RepositoryRestResource 注解来自定义仓库的暴露方式。

事件监听:

○ Spring Data REST 提供了事件监听机制,可以在资源创建、更新、删除等操作前后触发自定义逻辑。这可以通过实现 ApplicationListener 接口并监听 BeforeCreateEvent、AfterCreateEvent 等事件来实现。

分页和排序:

○ Spring Data REST 自动支持分页和排序。客户端可以通过 URL 参数(如 ?page=0&size=20&sort=name,asc)来请求分页和排序后的数据。

文档生成:

○ Spring Data REST 可以生成 API 文档,帮助客户端了解可用的端点和操作。你可以通过访问 /api-docs 端点来获取这些文档。

2、CRUD使用示例

以目前数言机器人项目为例,在使用mybatis同时加入Spring Data Rest
示例对应表:msg_robot 机器人表

添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
创建实体

虽然已经有机器人表实体,但作用在mybatis,这里新建实体。后面考虑实体如何合并

import cn.com.yunke.msgbot.constant.CommonConstant;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import java.time.LocalDateTime;@Table(name = "msg_robot")
@Entity
@Data
@RequiredArgsConstructor
public class MsgRobotRestPO {@GeneratedValue@Idprivate Long id;/*** 租户code*/private String orgCode;/*** 机器人名称*/private String robotName;}
创建仓库接口

纯接口不带方法,带方法可以实现自定义查询

@RepositoryRestResource(collectionResourceRel = "msgRobotRestPoes", path = "msgRobotRestPoes")
public interface MsgRobotRestRepository extends JpaRepository<MsgRobotRestPO, Long> {Collection<MsgRobotRestPO> findByRobotName(@Param("robotName") String  robotName);
}

访问 API
启动应用后,你可以通过以下 URL 访问 RESTful API:
● 获取所有机器人:GET /bmsMsgRobotRestPoes
● 分页获取机器人:GET /bmsMsgRobotRestPoes?page=1&size=2&sort=createTime
● 获取单个机器人:GET /bmsMsgRobotRestPoes/{id}
返回体:

{"orgCode": "","robotName": "555","_links": {"self": {"href": "http://127.0.0.1:9000/bmsMsgRobotRestPoes/1642067545749266432"},"bmsMsgRobotRestPO": {"href": "http://127.0.0.1:9000/bmsMsgRobotRestPoes/1642067545749266432"}}
}

● 创建机器人:POST /msgRobotRestPoes =>201
● 更新机器人:PATCH /msgRobotRestPoes/{id} =>200
● 删除机器人:DELETE /msgRobotRestPoes/{id} =>204
● 查询机器人:GET /msgRobotRestPoes/search/findByRobotName?robotName=111111
特性分析
1、自动生成的不能直接详细的条件查询
需要在Repository接口上定义方法,上面Repository代码
通过search查询实现,效率比之前快,但没有ZenStack灵活

3、关联关系

Spring Data Rest 会自动检测实体之间的关联广西,并创建关联资源,如:机器人下配置信息

@Table(name = "msg_robot")
@Entity
@Data
@RequiredArgsConstructor
public class MsgRobotRestPO {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;/*** 租户code*/private String orgCode;/*** 机器人名称*/private String robotName;@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)@JoinColumn(name = "robot_id")private List<MsgRobotParamConfigRestPO> paramConfig;
}
@Table(name = "msg_robot_param_config")
@Entity
@Data
@RequiredArgsConstructor
public class MsgRobotParamConfigRestPO {private static final long serialVersionUID = 1L;/*** id*/@Id@GeneratedValueprivate Long id;/*** 对应配置的id*/private String configId;/*** 对应配置的值*/private String configValue;/*** 机器人*/@ManyToOne(fetch = FetchType.LAZY)private MsgRobotRestPO robot;
}

映射关系查询结果

{"orgCode": "222","robotName": "555","paramConfig": [],"_links": {"self": {"href": "http://127.0.0.1:9000/msgRobotRestPoes/1762047555096432644"},"bmsMsgRobotRestPO": {"href": "http://127.0.0.1:9000/msgRobotRestPoes/1762047555096432644"}}
}

4、事件

通过监听事件,我们可以在资源的创建、更新、删除等操作时执行自定义逻辑

@Component
@RepositoryEventHandler(BmsMsgRobot.class)
public class BmsMsgRobotEventHandler {@HandleBeforeCreatepublic void handleBeforeCreate(BmsMsgRobot robot) {System.out.println("handleBeforeCreate robot");}
}

5、数据权限

1、Spring Data Rest 数据权限,可以通过集成Spring Security实现对资源的安全控制。配置安全规则,限制对部分资源的访问权限。

a. 添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
b.配置 Spring Security

创建一个配置类来配置 Spring Security,并实现自定义的认证和授权机制。
自定义认证过滤器
创建一个自定义的认证过滤器,用于调用外部接口验证用户的权限。

package cn.com.yunke.msgbot.rest.filter;
import cn.com.yunke.msgbot.exception.BotServiceException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collections;public class CustomAuthenticationFilter extends AbstractAuthenticationProcessingFilter {public CustomAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {super(new AntPathRequestMatcher(defaultFilterProcessesUrl));setAuthenticationManager(authenticationManager);}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {String sessionId = request.getParameter("sessionId");if (sessionId == null || sessionId.isEmpty()) {throw new BotServiceException("Session ID is required");}// 调用外部接口验证 session IDboolean isValid = callExternalApiToValidateSession(sessionId);if (!isValid) {throw new BotServiceException("Invalid session ID");}// 创建并返回 Authentication 对象UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(sessionId, "", Collections.emptyList());return getAuthenticationManager().authenticate(authRequest);}private boolean callExternalApiToValidateSession(String sessionId) {// 调用外部接口验证 session ID// 返回 true 或 false// 示例代码:// 假设外部接口返回一个 JSON 对象,包含 isValid 字段// 使用 HttpClient 或其他 HTTP 客户端库调用外部接口return true;}@Overrideprotected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {SecurityContextHolder.getContext().setAuthentication(authResult);chain.doFilter(request, response);}
}
自定义认证提供者

创建一个自定义的认证提供者,用于处理认证逻辑。

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;public class CustomAuthenticationProvider implements AuthenticationProvider {private final UserDetailsService userDetailsService;public CustomAuthenticationProvider(UserDetailsService userDetailsService) {this.userDetailsService = userDetailsService;}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {String sessionId = (String) authentication.getPrincipal();UserDetails userDetails = userDetailsService.loadUserByUsername(sessionId);if (userDetails == null) {throw new UsernameNotFoundException("User not found");}return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());}@Overridepublic boolean supports(Class<?> authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}
}
自定义用户详细信息服务

创建一个自定义的用户详细信息服务,用于从外部接口获取用户详细信息。

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;@Service
public class CustomUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String sessionId) throws UsernameNotFoundException {// 调用外部接口获取用户详细信息List<GrantedAuthority> authorities = callExternalApiToGetUserAuthorities(sessionId);if (authorities == null || authorities.isEmpty()) {throw new UsernameNotFoundException("User not found");}return new User(sessionId, "", authorities);}private List<GrantedAuthority> callExternalApiToGetUserAuthorities(String sessionId) {// 调用外部接口获取用户权限// 返回权限列表// 示例代码:// 假设外部接口返回一个 JSON 对象,包含 authorities 字段List<GrantedAuthority> authorities = new ArrayList<>();authorities.add(new SimpleGrantedAuthority("ROLE_admin"));return authorities;//return Collections.emptyList();}
}
配置 Spring Security

创建一个配置类来配置 Spring Security,并注册自定义的认证过滤器和认证提供者。

import cn.com.yunke.msgbot.rest.filter.CustomAuthenticationFilter;
import cn.com.yunke.msgbot.rest.provider.CustomAuthenticationProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic CustomAuthenticationFilter customAuthenticationFilter() throws Exception {CustomAuthenticationFilter filter = new CustomAuthenticationFilter("/", authenticationManager());return filter;}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/api-docs", "/h2-console/**").permitAll().anyRequest().authenticated().and().addFilterBefore(customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class).httpBasic().and().csrf().disable().headers().frameOptions().disable(); // For H2 console}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService));}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}
使用 @PreAuthorize 注解

在服务或控制器中使用 @PreAuthorize 注解来控制实体或者方法级别的访问。

@PreAuthorize("hasRole('ROLE_admin')")
@RepositoryRestResource(collectionResourceRel = "bmsMsgRobotRestPoes", path = "bmsMsgRobotRestPoes")
public interface BmsMsgRobotRestRepository extends JpaRepository<BmsMsgRobotRestPO, Long> {@PreAuthorize("hasRole('ROLE_member')")Collection<BmsMsgRobotRestPO> findByRobotName(@Param("robotName") String  robotName);//Page<BmsMsgRobotRestPO> findAll(@NotNull Pageable pageable);
}

注意:1、引入Spring Security需要考虑与之前全局拦截器兼容,不要冲突,考虑之前需要不拦截的接口等。

2、已有的全局拦截器,控制数据权限,权限体系与已有接口权限体系一致

6、其他功能

如更改路径、更改自动生产API名称、字段是否显示等更多内容参考官方文档
Spring Data REST官网


http://www.ppmy.cn/news/1560601.html

相关文章

grouped.get_group((‘B‘, ‘A‘))选择分组

1. df.groupby([team, df.name.str[0]]) df.groupby([team, df.name.str[0]]) 这一部分代码表示对 DataFrame df 按照 两个条件 进行分组&#xff1a; 按照 team 列&#xff08;即团队&#xff09;。按照 name 列的 首字母&#xff08;df.name.str[0]&#xff09;。 df.name.s…

1.1 假设一:走势包含一切信息

在资本市场上&#xff0c;每天我们都能听到或者看到大量信息&#xff0c;这些信息各式各样让人眼花缭乱。我们进入市场&#xff0c;首先会面临一个问题&#xff1a;是否需要把所有的信息都浏览一遍&#xff0c;或者说根据消息来预测走势是靠谱的么&#xff1f; 为了回答这个问题…

Linux性能优化-系列文章-汇总

前言 Linux性能优化&#xff0c;涉及了CPU&#xff0c;内存&#xff0c;磁盘&#xff0c;网络等很多方面&#xff0c;一方面涉及的知识面广&#xff0c;同时又要在原理方面掌握一定的深度。所以整理总结了Linux性能优化的一系列文章。当处理Linux性能问题的时候&#xff0c;可…

Vue项目中生成node_modules文件夹的两种常用方法及npm优势

在Vue项目中生成node_modules文件夹的过程非常简单,主要步骤如下: 1、使用 npm 安装依赖包; 2、使用 yarn 安装依赖包。其中,推荐使用npm安装依赖包,原因如下: 兼容性更广:npm是Node.js的默认包管理工具,具有更高的兼容性。社区支持:npm拥有更大的用户基础和社区支持,…

Rust 基础入门指南

Rust 基础入门指南 1. Rust 语言概述 Rust 的历史与设计理念 Rust 是由 Mozilla 研究院的 Graydon Hoare 于2010年开始创建的系统编程语言。其设计目标是创建一种安全、并发、实用的编程语言&#xff0c;特别关注内存安全和并发性。 Rust 的核心设计理念包括&#xff1a; …

leetcode题目(2)

目录 1.删除有序数组中的重复项 2.移除元素 3.找出字符中第一个匹配项的下标 4.搜索插入位置 5.最大子数组和 6.最后一个单词的长度 1.删除有序数组中的重复项 https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/ int removeDuplicates(in…

C++软件设计模式之命令模式

C设计模式中的命令模式是一种行为设计模式&#xff0c;它封装了一个请求作为一个对象&#xff0c;从而让你使用不同的请求把客户端与服务操作解耦。这种模式有着广泛的用途和多种变体。以下是对命令模式的详细解析&#xff1a; 动机与意图 解耦调用者和接收者&#xff1a;命令…

并发编程 之 线程池实战配置建议(二)

本篇围绕线程池在不同场景下的使用&#xff0c;各种参数调优&配置&#xff0c;包括线程池的关闭等&#xff1b; 将结合场景、源码和示例代码来说明其中关键。 目录 1. 创建线程池 1.1 corePoolSize 1.2 maximumPoolSize 1.3 workQueue 1.3.1 ArrayBlockingQueue 1.3.2…