自动生成RESTful API——Spring Data Rest

news2025/1/6 19:25:25

一、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
    @Id
    private 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
    @GeneratedValue
    private 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 {
    @HandleBeforeCreate
    public 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);
    }

    @Override
    public 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 ID
        boolean 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;

    }

    @Override
    protected 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;
    }

    @Override
    public 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());
    }

    @Override
    public 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 {

    @Override
    public 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 {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter("/", authenticationManager());
        return filter;
    }

    @Override
    protected 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
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(new CustomAuthenticationProvider(userDetailsService));
    }

    @Bean
    public 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.coloradmin.cn/o/2271154.html

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

相关文章

信号的产生、处理

一、信号的概念 信号是linux系统提供的一种&#xff0c;向指定进程发送特定事件的方式。收到信号的进程&#xff0c;要对信号做识别和处理。信号的产生是异步的&#xff0c;进程在工作过程中随时可能收到信号。 信号的种类分为以下这么多种&#xff08;用指令kill -l查看&…

Node.js应用程序遇到了内存溢出的问题

vue 项目 跑起来&#xff0c;一直报错&#xff0c;内存溢出 在 文件node_modules 里 .bin > vue-cli-service.cmd 在依赖包这个文件第一行加上这个 node --max-old-space-size102400 "%~dp0\..\vue\cli-service\bin\vue-cli-service.js" %* node --max-old-s…

openGauss与GaussDB系统架构对比

openGauss与GaussDB系统架构对比 系统架构对比openGauss架构GaussDB架构 GaussDB集群管理组件 系统架构对比 openGauss架构 openGauss是集中式数据库系统&#xff0c;业务数据存储在单个物理节点上&#xff0c;数据访问任务被推送到服务节点执行&#xff0c;通过服务器的高并…

深入理解计算机系统—虚拟内存(一)

一个系统中的进程是与其他进程共享 CPU 和主存资源的。然而&#xff0c;共享主存会形成特殊的挑战。随着对 CPU 需求的增长&#xff0c;进程以某种合理的平滑方式慢了下来。但是如果太多的进程需要太多的内存&#xff0c;那么它们中的一些就根本无法运行。 为了更加有效地管理内…

九、Vue 事件处理器

文章目录 前言一、基础事件绑定:v-on 指令二、方法调用:组织有序的交互逻辑三、事件修饰符阻止冒泡与默认事件捕获与自身触发单次触发与鼠标按键区分四、按键修饰符前言 在 Vue.js 的交互世界里,事件处理器起着举足轻重的作用,它让页面从静态展示迈向动态交互,精准捕捉用户…

Quartus In-System Sources and Probes Editor 的使用说明

文章目录 前言使用说明参考资料 前言 Quartus 提供了 In-System Sources and Probes Editor 调试工具&#xff0c;通过 JTAG 接口使用该工具可以驱动和采样内部节点的逻辑值。即通过 Sources 功能来驱动 FPGA 内部信号&#xff0c;通过 Probes 功能来探测内部节点的逻辑值。在…

springboot整合Quartz实现定时任务

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1.核心概念2.基础实现2.1引入依赖2.2创建具体逻辑类2.3配置类 总结 前言 在项目中我们会有许多要进行定时执行逻辑的业务场景&#xff0c;比如定期生成日报、定…

阿里云 ECS 服务器绑定多个公网IP

阿里云 ECS 服务器绑定多个公网IP 一、弹性公网IP绑定ECS服务器 单台ECS一般只能直接绑定一个弹性公网IP&#xff0c;但是可以绑定多张弹性网卡&#xff0c;如果把弹性公网IP绑定到弹性网卡上&#xff0c;那么单台ECS就能间接绑定多个弹性公网IP。但有的服务器系统镜像可能不…

线性代数考研笔记

行列式 背景 分子行列式&#xff1a;求哪个未知数&#xff0c;就把b1&#xff0c;b2放在对应的位置 分母行列式&#xff1a;系数对应写即可 全排列与逆序数 1 3 2&#xff1a;逆序数为1 奇排列 1 2 3&#xff1a;逆序数为0 偶排列 将 1 3 2 只需将3 2交换1次就可以还原原…

LLM - 使用 LLaMA-Factory 部署大模型 HTTP 多模态服务 (4)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/144881432 大模型的 HTTP 服务&#xff0c;通过网络接口&#xff0c;提供 AI 模型功能的服务&#xff0c;允许通过发送 HTTP 请求&#xff0c;交互…

数据库知识汇总2

一. 范式 定义&#xff1a;范式是符合某一种级别的关系模式的集合。 关系数据库中的关系必须满足一定的要求。满足不同程度要求的为不同范式&#xff1b; 一个低一级范式的关系模式&#xff0c;通过模式分解&#xff08;schema decomposition&#xff09;可以转换为若干个高一…

TP 钱包插件版本的使用

目前 TokenPocket 的几个平台中&#xff0c;以 ios 和 安卓版本最为常见&#xff0c;其实很少有人知道&#xff0c;浏览器上有一个插件版本的 Tp, 用电脑多的话&#xff0c;这也是一个挺好的选择。 最新版本现在支持Chrome、Brave 浏览器、Edge&#xff08;Firefox及Opera正在…

反向传播算法的偏置更新步骤

偏置的更新步骤 假设我们有一个三层神经网络&#xff08;输入层、隐藏层和输出层&#xff09;&#xff0c;并且每层的激活函数为 sigmoid 函数。我们需要更新隐藏层和输出层的偏置。以下是详细的步骤&#xff1a; 1. 计算误差项&#xff08;Error Term&#xff09; 输出层的…

(二)当人工智能是一个函数,函数形式怎么选择?ChatGPT的函数又是什么?

在上一篇文章中&#xff0c;我们通过二次函数的例子&#xff0c;讲解了如何训练人工智能。今天&#xff0c;让我们进一步探讨&#xff1a;面对不同的实际问题&#xff0c;应该如何选择合适的函数形式&#xff1f; 一、广告推荐系统中的函数选择 1. 业务目标 想象一下&#x…

Vue3 中的插槽

Vue3 中插槽的使用&#xff0c;插槽是 Vue 中的一个特别特性&#xff0c;插槽就是模版内容。例如<h1>标题 1</h1>标题 1 就是插槽&#xff0c;Vue 是无法识别模板内容的&#xff0c;只能通过属性进行传递。Slot 主要包括默认、具名和作用域。Slot开发起来难度不大&…

单元测试3.0+ @RunWith(JMockit.class)+mock+injectable+Expectations

Jmockit使用笔记_基本功能使用Tested_Injectable_Mocked_Expectations_jmockit.class-CSDN博客 静态变量直接赋值就好&#xff0c;没必要mock了 测试框架Jmockit集合junit使用 RunWith(JMockit.class) 写在测试案例类上的注解 Tested 在测试案例中,写在我们要测试的类上…

靶机系列|VULNHUB|DC-3

描述 DC-3 是另一个专门建造的易受攻击实验室&#xff0c;旨在获得渗透测试领域的经验。 与之前的 DC 版本一样&#xff0c;这个版本的设计考虑到了初学者&#xff0c;尽管这一次只有一个标志、一个入口点&#xff0c;根本没有线索。 必须具备 Linux 技能和熟悉 Linux 命令行…

sqlserver sql转HTMM邮件发送

通过sql的形式&#xff0c;把表内数据通过邮件的形式发送出去 declare title varchar(100) DECLARE stat_date CHAR(10),create_time datetime SET stat_dateCONVERT(char(10),GETDATE(),120) SET create_timeDATEADD(MINUTE,-20,GETDATE()) DECLARE xml NVARCHAR (max) DECLAR…

MCU芯片是什么意思_有哪些作用?

MCU(Microcontroller Unit)芯片&#xff0c;即微控制单元&#xff0c;是一种集成了中央处理器(CPU)、存储器(ROM、RAM)以及各种外设接口(如输入输出引脚、定时器、串口等)的集成电路芯片。它通过超大规模集成电路技术&#xff0c;将具有数据处理能力的中央处理器、随机存储器、…

朱姆沃尔特隐身战舰:从失败到威慑

前言 "朱姆沃尔特"号驱逐舰是美国海军雄心勃勃的项目&#xff0c;旨在重塑未来海战。它融合了隐身、自动化和强大火力&#xff0c;然而由于技术问题和预算超支&#xff0c;原计划建造32艘的目标被大幅缩减&#xff0c;最终只建造了三艘。该舰的设计特点包括“穿浪逆船…