自动生成RESTful API——Spring Data Rest

一、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官网

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

FICO财务模块在SAP ECC与S4 HANA系统间的差异有哪些?

【SAP系统研究】 #SAP #FICO #ECC #HANA #Oracle #SAP财务 尽管SAP S4/HANA已经发布很久&#xff0c;但使用SAP ECC系统的企业也仍然很多。 这两个系统在FICO模块中有哪些常见的不同呢&#xff1f; 1、数据库表 ①SAP ECC系统 可以在Oracle、IBM DB2等数据库上运行 ②SAP S…

CDPHudi实战-集成spark

[一]使用Spark-shell 1-配置hudi Jar包 [rootcdp73-1 ~]# for i in $(seq 1 6); do scp /opt/software/hudi-1.0.0/packaging/hudi-spark-bundle/target/hudi-spark3.4-bundle_2.12-1.0.0.jar cdp73-$i:/opt/cloudera/parcels/CDH/lib/spark3/jars/; done hudi-spark3.4-bu…

mac m2 安装 docker

文章目录 安装1.下载安装包2.在downloads中打开3.在启动台打开打开终端验证 修改国内镜像地址小结 安装 1.下载安装包 到官网下载适配的安装包&#xff1a;https://www.docker.com/products/docker-desktop/ 2.在downloads中打开 拖过去 3.在启动台打开 选择推荐设置 …

Power BI如何连接Azure Databricks数据源?

故事背景: 近期有朋友询问&#xff0c;自己公司有一些项目使用了Azure Databricks用于数据存储。如何使用Power BI Desktop桌面开发软件连接Azure Databricks的数据源呢&#xff1f; 解决方案: 其实Power BI是提供了连接Azure Databricks数据源的选项的&#xff0c;只是配置…

Python入门教程 —— 进制转换

找其他编译器&#xff0c;系统解释器&#xff0c;这样速度会快很多。 进制 现代的计算机和依赖计算机的设备里都用到二进制(即0和1)来保存和表示数据&#xff0c;一个二进制表示一个比特(Bit)。 在二进制的基础上&#xff0c;计算机还支持八进制和十六进制这两种进制。 除了…

HTML5新特性|05 CSS3边框CSS3背景

CSS3边框 1、CSS3边框: 通过CSS3&#xff0c;您能够创建圆角边框&#xff0c;向矩形添加阴影&#xff0c;使用图片来绘制边框-并且不需使用设计软件&#xff0c;比如PhotoShop。 属性: border-radius 圆角box-shadow:水平阴影 垂直阴影 阴影的清晰度 阴影的大小 阴影的颜色…

《Vue3实战教程》26:Vue3Transition

如果您有疑问&#xff0c;请观看视频教程《Vue3实战教程》

SpringCloudAlibaba实战入门之Sentinel服务降级和服务熔断(十五)

一、Sentinel概述 1、Sentinel是什么 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 一句话概括:sentinel即Hystrix的替代品,官网: https://sentinelguard.io/zh…

Scratch教学作品 | 白水急流——急流勇进,挑战反应极限! ‍♂️

今天为大家推荐一款刺激又好玩的Scratch冒险作品——《白水急流》&#xff01;由AgentFransidium制作&#xff0c;这款作品将带你体验惊险的急流救援任务&#xff0c;帮助那位“睡着的疯狂人”安全穿越湍急水域&#xff01;想要挑战自己的反应极限&#xff1f;快来试试吧&#…

计算机毕业设计Django+Tensorflow音乐推荐系统 音乐可视化 卷积神经网络CNN LSTM音乐情感分析 机器学习 深度学习 Flask

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

Nginx服务器配置SSL证书

1.执行以下命令&#xff0c;在Nginx的conf目录下创建一个用于存放证书的目录。 cd /usr/local/nginx/conf #进入Nginx默认配置文件目录。该目录为手动编译安装Nginx时的默认目录&#xff0c;如果您修改过默认安装目录或使用其他方式安装&#xff0c;请根据实际配置调整。 mkd…

Gemini和ChatGPT全面对比分析,有什么区别和优势?

当 AI 聊天机器人首次出现时&#xff0c;每个人都在竞相发布自己的足够好的第一版 AI 聊天机器人&#xff0c;很容易在 Gemini 与 ChatGPT 等应用程序之间进行比较。但随着 Google 和 OpenAI 不断添加新功能、模型和访问其聊天机器人的方式&#xff0c;差异变得不那么明显。 现…

从0到机器视觉工程师(二):封装调用静态库和动态库

目录 静态库 编写静态库 使用静态库 方案一 方案二 动态库 编写动态库 使用动态库 方案一 方案二 方案三 总结 静态库 静态库是在编译时将库的代码合并到最终可执行程序中的库。静态库的优势是在编译时将所有代码包含在程序中&#xff0c;可以使程序独立运行&…

低代码开发:开启企业数智化转型“快捷键”

一、低代码开发浪潮来袭&#xff0c;企业转型正当时 在当今数字化飞速发展的时代&#xff0c;低代码开发已如汹涌浪潮&#xff0c;席卷全球。从国际市场来看&#xff0c;诸多企业巨头纷纷布局低代码领域&#xff0c;像微软的 PowerApps、OutSystems 等平台&#xff0c;凭借强大…

UE5动画蓝图

动画蓝图&#xff0c;混合空间&#xff0c;状态机&#xff0c;瞄准偏移&#xff0c;动画蒙太奇&#xff0c;动画混合&#xff0c;骨骼绑定&#xff0c;动画重定向&#xff0c;动画通知&#xff0c;Control Rig…… 虚幻动画模块是一个庞大的系统&#xff0c;大模块里又包含很多…

[redux] useDispatch的两种用法

先重写2个方法先, 方便ts类型推导,如果你看不懂为什么这么写, 先看我这篇 [redux] ts声明useSelector和useDispatch-CSDN博客 export type RootState ReturnType<typeof store.getState>; export type AppDispatch typeof store.dispatch; export const useAppDispat…

javaEE-网络原理-1初识

目录 一.网络发展史 1.独立模式 2.网络互联 二.局域网LAN 1.基于网线直连&#xff1a; 2.基于集线器组件&#xff1a; 3.基于交换机组件&#xff1a; 4.基于交换机和路由器组件 ​编辑 三、广域网WAN 四、网络通信基础 1.ip地址 2.端口号&#xff1a; 3.协议 4.五…

jenkins入门3

1、新建视图 视图可以理解为是item的集合&#xff0c;这样可以将item分类。新建视频可以选择加入已有的item 2、新建item 1)输入任务名称、选择一个类型&#xff0c;常用的是第一个freestyle project 2&#xff09;进行item相关配置&#xff0c;general 设置项目名字,描述,参数…

【C语言的小角落】--- 深度理解取余/取模运算

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; C语言的小角落 本篇博客我们来深度理解取余/取模&#xff0c;以及它们在不同语言中出现不同现象的原因。 &#x1f3e0; 关于取整 &#x1f3b5; 向0取整…

mapbox进阶,添加路径规划控件

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️MapboxDirections 控件二、🍀添加路径规划控件1. ☘️实现思路2. ☘️…