可配置输入参数的接口如何设计

个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview


作为程序员,我们绝大多数场景需要根据业务需求来设计系统,开发后端接口,这些接口通常都是在设计阶段就确定了,但是还有一些场景需要实现支持的配置的接口,包含以下功能

1、可以配置接口url

这个通常在一些论坛博客类系统中比较常见,用于设置个人主页,但其实这个又是不同概念,个人主页通常是get请求,然后通过path索引用户主页。

2、可选择接口具体实现

流程引擎、规则引擎通常就可以实现

3、输入参数和输出参数可配置

应用场景包括数据收集等

前面乱讲一通,最终回归本题——可配置输入参数的接口如何设计?

AI的回答

以下是AI的回答——可配置输入参数的接口如何设计?

设计支持可配置输入参数的接口时,主要目标是保证接口的灵活性和易用性,同时兼顾健壮性和可维护性。以下是一个通用的设计策略:

  1. 使用Map或Dictionary类型的参数
    可以定义一个Map<String, Object>作为接口的输入参数,其中键(key)表示参数名,值(value)表示参数值。这样可以根据需要动态添加或减少参数。
public interface MyService {Response handleRequest(Map<String, Object> parameters);
}

这种方法简单灵活,但对于复杂的参数校验和类型转换需要额外处理。

  1. 使用泛型请求类
    定义一个包含所有可能参数的请求类,使用Java Bean或者Builder模式,通过构造方法或setters注入参数,同时可以用@JsonCreator@AllArgsConstructor等注解支持JSON序列化和反序列化。
public class RequestParams<T> {private String param1;private int param2;private T customParam; // 泛型参数,可根据需求定制// 构造方法、getter、setter省略...// 或者使用Builder模式public static class Builder<T> {// ...public RequestParams build() { /*...*/ }}
}public interface MyService {Response handleRequest(RequestParams<?> params);
}
  1. 使用策略模式
    如果参数的处理方式因参数类型而异,可以设计不同的策略类处理不同类型的参数,接口接收这些策略类作为参数。
public interface ParamStrategy {void process(RequestContext context);
}public interface MyService {Response handleRequest(List<ParamStrategy> strategies);
}
  1. 使用命令模式
    对于复杂的操作,可以将参数封装成一个命令对象,命令对象包含所有执行操作所需的参数。
public interface Command {void execute();
}public class SomeCommand implements Command {private final String param1;private final int param2;public SomeCommand(String param1, int param2) {this.param1 = param1;this.param2 = param2;}@Overridepublic void execute() {// 使用param1和param2执行操作}
}public interface MyService {Response handleRequest(Command command);
}

在设计时,请务必充分考虑参数的有效性检查、默认值设定以及错误处理机制,确保接口的健壮性。同时,良好的文档说明对于使用者来说非常重要,特别是当参数众多且复杂时。

实现思路

有了AI的帮助,思路一下子打开了是吧!

这里主要讨论Map作为参数具体该怎么实现。

若仅着眼于数据采集的目标,采用Map作为输入参数无疑是一种可行方案,然而这种方式也存在显著不足。尽管如此便捷,但数据的原始形态较为零散无序,在实际应用前仍需额外进行整理加工,因此从整体效果上看并不尽如人意。

所以还是需要一些设计的,简化流程如下,我讲的不好,也可以直接跳到下面demo看实现。

具体实现Demo

Controller

/*** @author wnhyang* @date 2023/11/29**/
@RestController
@RequestMapping("/risk")
@RequiredArgsConstructor
@Slf4j
public class RiskController {private final RiskService riskService;@PostMapping("/{name}/sync")public String syncRisk(@PathVariable("name") String name, @RequestBody Map<String, Object> params) {log.info("syncRisk {}", name);log.info("param {}", params);return riskService.syncRisk(name, params);}}

Service

/*** @author wnhyang* @date 2023/11/29**/
@Service
@Slf4j
@RequiredArgsConstructor
public class RiskServiceImpl implements RiskService {/*** 模拟服务配置*/private static final Map<String, Set<InputFieldVO>> SERVICE_CONFIG = Map.of("publicInterface", new HashSet<>(Arrays.asList(new InputFieldVO().setId(1L).setParamName("eventId").setDisplayName("事件标识").setName("N_S_eventId").setRequired(true).setType(0),new InputFieldVO().setId(2L).setParamName("appName").setDisplayName("应用标识").setName("N_S_appName").setRequired(true).setType(0),new InputFieldVO().setId(3L).setParamName("customerId").setDisplayName("客户号").setName("N_S_customerId").setRequired(true).setType(0),new InputFieldVO().setId(5L).setParamName("age").setDisplayName("年龄").setName("N_N_age").setRequired(true).setType(1),new InputFieldVO().setId(4L).setParamName("money").setDisplayName("金额").setName("N_F_money").setRequired(true).setType(2),new InputFieldVO().setId(6L).setParamName("transTime").setDisplayName("交易时间").setName("N_D_transTime").setRequired(true).setType(3),new InputFieldVO().setId(7L).setParamName("transType").setDisplayName("交易类型").setName("N_E_transType").setRequired(true).setType(4),new InputFieldVO().setId(8L).setParamName("isPayee").setDisplayName("是否收款人").setName("N_B_isPayee").setRequired(true).setType(5),new InputFieldVO().setId(9L).setParamName("event").setDisplayName("事件标识").setName("N_S_event").setRequired(true).setType(0))));@Overridepublic String syncRisk(String name, Map<String, Object> params) {// 通过SERVICE_CONFIG取name判空log.info("name {}", name);log.info("params {}", params);Set<InputFieldVO> fields = SERVICE_CONFIG.get(name);if (fields == null) {return "服务未找到";}// 使用增强for循环代替迭代器for (InputFieldVO field : fields) {Object value = params.get(field.getParamName());if (value != null) {// 设置field的值field.setValue(value);log.info("field {}", field);}}return fields.toString();}@Overridepublic String asyncRisk(String name, Map<String, Object> params) {log.info("name {}", name);return null;}
}

InputFieldVO

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class InputFieldVO {/*** 参数名*/private String paramName;/*** 是否必填*/private Boolean required;/*** 值,默认null*/private Object value;/*** 自增编号*/private Long id;/*** 显示名*/private String displayName;/*** 字段名*/private String name;/*** 字段类型*/private Integer type;/*** 字段分组*/private Long groupId;/*** 描述*/private String description;/*** 默认值*/private String defaultValue;/*** 是否动态字段 N(normal)和D(dynamic)*/private Boolean dynamic = Boolean.FALSE;}

FieldTypeEnum

/*** @author wnhyang* @date 2023/12/5**/
@AllArgsConstructor
@Getter
public enum FieldTypeEnum {/*** 字符型*/STRING(0, "S", "字符型"),/*** 整数型*/NUMBER(1, "N", "整数型"),/*** 小数型*/FLOAT(2, "F", "小数型"),/*** 日期型*/DATE(3, "D", "日期型"),/*** 枚举型*/ENUM(4, "E", "枚举型"),/*** 布尔型*/BOOLEAN(5, "B", "布尔型");private final Integer code;private final String name;private final String desc;}

测试

请求http://localhost:8080/risk/publicInterface/sync

{"eventId": "手机交易","appName": "手机","customerId": "123456","age": "24","money": "3.14159","transTime": "2024-03-01 20:35:45","transType": "2","isPayee": "true","event": "true"
}

日志打印如下

小结

就先这样草草结束了,还有以下需要说明一下。

1、字段类型区分的目的是为了便于后续规则或其他计算类方法能直接使用整数或浮点类字段,而且这类字段还可以通过比较大小来区分,当然枚举和布尔类也有其他用处。

2、动态字段是为脚本类字段预留的。

3、以上Demo没有加入数数据库,这个可以设计一下。

写在最后

拙作艰辛,字句心血,望诸君垂青,多予支持,不胜感激。


个人博客:无奈何杨(wnhyang)

个人语雀:wnhyang

共享语雀:在线知识共享

Github:wnhyang - Overview

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

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

相关文章

[Ubuntu 20.04] 创建一个开机服务(systemd服务单元文件)

例如用于创建一个名为my_network_app.service的systemd服务,该服务将在系统启动时运行一个网络应用程序。 1. 创建应用程序的启动脚本 首先,你需要一个启动你的网络应用程序的脚本。这个脚本可以是任何语言编写的,只要它能正确地启动你的应用程序。例如,假设你有一个名为…

想要节省成本,哪个品牌的https证书值得考虑?

为了确保网站数据传输安全&#xff0c;启用HTTPS加密是关键步骤。在众多SSL证书供应商中&#xff0c;如何找到价格合理且品质优良的HTTPS加密证书呢&#xff1f;本文将探讨这个问题&#xff0c;并重点关注具有高性价比优势的沃通CA。 沃通CA作为业内知名的SSL证书服务商&#x…

MySQL数据管理二

1.数据库的完整性 数据库中的数据是从外界输入的&#xff0c;而数据的输入由于种种原因&#xff0c;会发生输入无效或错误信息。保证输入的数据符合规定&#xff0c;成为了数据库系统&#xff0c;尤其是多用户的关系数据库系统首要关注的问题。 它是应防止数据库中存在不符合语…

双证知音中国社科院与英国斯特灵大学创新与领导力博士

对于博士阶段来说&#xff0c;由于科研任务繁重&#xff0c;需要自主学习的内容以及接受的挑战也相对较多&#xff0c;所以对于晋升读博这件事并不那么容易。无论是考博还是读中外合作办学博士&#xff0c;上课语言、博导、论文这些关都是要一一闯过的。能遇见优秀的导师是我们…

CYQ.Data 支持 DaMeng 达梦数据库

DaMeng 达梦数据库介绍: 达梦数据库(DMDB)是中国自主研发的关系型数据库管理系统,由达梦科技股份有限公司开发。 达梦数据库提供了企业级的数据库解决方案,广泛应用于金融、电信、政府、制造等行业领域。 达梦数据库具有以下特点和优势: 高性能:具备高性能的并发处理…

滑窗问题【Leetcode3最长子串/438找出所有异位词】

3. 无重复字符的最长子串 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 请注意&#xff0c;你的答案必须是 子串 的长度&#xff0c;子序列不是子串。 思路&#xff1a;滑窗范围[pl, pr)&#xff0c;用cnt数组维护滑窗内的每个字符的出现次数…

React 的入门介绍

React 是什么 React是一个用于构建用户界面的JavaScript库。它由Facebook开发&#xff0c;并于2013年首次发布。React将用户界面拆分为小的可重用组件&#xff0c;每个组件都有自己的状态&#xff0c;并根据状态的变化来更新界面。 React使用了虚拟DOM&#xff08;Virtual DO…

RabbitMQ如何实现消费端限流

什么是消费端限流&#xff0c;这个一种保护消费者的手段&#xff0c;假如说&#xff0c;现在是业务高峰期了&#xff0c;消息有大量堆积&#xff0c;导致MQ消费需要不断的进行消息消费&#xff0c;很容易被打挂&#xff0c;甚至重启之后还是会被大量消息涌入&#xff0c;继续被…

Xilinx 7系列FPGA配置(ug470)

Xilinx 7系列FPGA配置&#xff08;ug470&#xff09; 配置模式串行配置模式接口从-连接方式主-连接方式串行菊花链&#xff08;非同时配置&#xff09;串行配置&#xff08;同时配置&#xff09;时序 主SPI配置模式SPIx1/x2 连接图SPIx1模式时序SPIx4 连接图SPI操作指令操作fla…

FC-AE-1553 协议

FC-AE-1553 协议 MIL-STD-1553B总线协议总线结构字格式消息传输方式 FC协议FC协议栈拓扑结构服务类型帧/序列/交换FC帧格式 FC-AE-1553网络构成帧类型命令帧状态帧数据帧 Information UnitsNC1NC2NC3-4NC5-7NT1-7 传输模式1. NC-NT2. NT-NC3. NT-NT4. 无数据字的模式命令5. 带数…

python并发编程之多进程、多线程、异步和协程详解

在Python中&#xff0c;有多种并发编程的方式可供选择&#xff0c;包括多进程、多线程、异步和协程。下面将对这些方式进行详细解释。 多进程&#xff1a;多进程是通过创建多个进程来实现并发的方式。每个进程都有自己独立的内存空间&#xff0c;可以并行执行任务。Python中的m…

detectron2 DiffusionDet 训练自己的数据集

配环境 git clone https://github.com/ShoufaChen/DiffusionDet# 创建环境 conda create -n diffusion python3.9 conda activate diffusion conda install pytorch1.11.0 torchvision0.12.0 torchaudio0.11.0 cudatoolkit11.3 -c pytorch pip install opencv-python# 安装det…

STM32CubeMX学习笔记12 ---低功耗模式

在实际使用中很多产品都需要考虑低功耗的问题&#xff0c;STM32F10X提供了三种低功耗模式&#xff1a;睡眠模式&#xff08;Sleep mode&#xff09;、停机模式&#xff08;Stop mode&#xff09;和待机模式&#xff08;Standby mode&#xff09;。这些低功耗模式可以有效减少系…

jnitrace的用法(查看jni的执行流程,方便unidbg补环境)

一、简单执行 jnitrace -l <要trace的so库> <包名> jnitrace -l libxxx.so com.xxx.app二、插入js脚本执行 jnitrace -p E:\kill_sll.js -l libxxx.so com.xxx.app三、attach模式执行 默认使用spawn执行&#xff0c;attach模式可能有有时bug没反应 jnitrace -l li…

Casper Network(CSPR)即将迎来两项重大升级,以实现功能上的进一步完善

Casper Network&#xff08;CSPR&#xff09;即将实现更加完备的功能升级&#xff0c;现已进入倒计时阶段。 Casper Network&#xff08;CSPR&#xff09;将升级到其最先进以及更全的版本&#xff0c;即“功能完备”的版本&#xff0c;让Casper Network&#xff08;CSPR&#…

腾讯云十大优惠活动曝光,TOP10值得买云服务器配置报价

腾讯云服务器多少钱一年&#xff1f;61元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器165元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…

Java实现读取转码写入ES构建检索PDF等文档全栈流程

背景 之前已简单使用ES及Kibana和在线转Base64工具实现了检索文档的demo&#xff0c;并已实现WebHook的搭建和触发流程接口。 传送门&#xff1a; 基于GitBucket的Hook构建ES检索PDF等文档全栈方案 使用ES检索PDF、word等文档快速开始 实现读取本地文件入库ES 总体思路&…

索引类型介绍

4、说说你知道的MySQL的索引类型&#xff0c;并分别简述一下各自的场景。 普通索引&#xff1a;没有任何限制条件的索引&#xff0c;该索引可以在任何数据类型中创建。 唯一索引&#xff1a;使用UNIQUE参数可以设置唯一索引。创建该索引时&#xff0c;索引列的值必须唯一&…

44、网络编程/数据库相关操作练习20240306

一、代码实现数据库的创建&#xff08;员工信息表&#xff09;&#xff0c;并存储员工信息&#xff08;工号、姓名、薪资&#xff09;&#xff0c;能实现增加人员信息、删除人员信息、修改人员薪资操作。 代码&#xff1a; #include<myhead.h>int do_update(sqlite3 *p…

基于canvas纯前端实现验证码的绘制

验证码功能是实现登录功能中比较常见的一个问题 验证码的整体思路是&#xff1a; 1.前端登录页面发起获取验证码图片请求. 2.服务端收到请求后,生成一个唯一id,对应的验证码图片 以及验证码图片对应的值(这个值使用缓存保存,id-值一一对应,缓存可使用redis或本地缓存,本地缓存…