【实战总结】大型系统超多参数接口API调用

一、背景

在与大型分布式系统协作完成功能实现时,如何实现50个参数及以上的API调用,其中还有嵌套参数场景,并支持业务默认参数配置实现、还需支持API参数按业务需要进行添加和删除。

二、方案设计

我们知道,在接口联调阶段我还需要对接口字段进行分析核对然后进行业务匹配,最后完成接口调用和功能实现。但是只能应对参数较少的接口API,且一般都是在程序中硬编码写死的。那么该如何实现超多参数配置扩展接口调用?我们可以从以下方案入手。

  • 1.基于远程的API参数定义对应的DTO,使用jackson的注解配置映射好相应的属性名称,属性参数一定要是全集的。
  • 2.远程API调用我们选择使用OpenFeign框架,声明式接口开发提高效率、完善的日志体系有助于问题排查、同时应用SpringMvc注解完成参数映射。
  • 3.定义API全集参数的默认值,使用键值对的方式,可以使用配置文件或使用快码表配置等。
  • 4.定义API默认参数(业务上必填参数)的列表,同上可以选择配置文件或快码表存储。
  • 5.设计一个参数比对映射的API,为全集和默认参数比对映射提供支持。场景说明:业务只会录入默认的参数值,其他的参数没有录入则为空,所有需要和从全集参数中获取默认值。同样,如果默认参数列表添加也只能从全集参数列表中选择添加。
  • 6.接口参数中存在多级嵌套属性,通过实现转换的API完成映射。

三、代码实现

参考代码中Map映射 是关键点。另外:属性1.属性2.属性3(goods.spec.params.height)类似格式的key转换多层级需要使用递归逻辑处理。

  • 1、代码结构主要分为两部分,远程API服务端和API调用方客户端。

在这里插入图片描述

  • 2、模拟远程API代码,这里主要体现参数DTO,多且嵌套组合(3级嵌套)
@RestController
@RequestMapping("/api/order")
public class OrderController {

    @PostMapping("/create")
    public String create(@RequestBody OrderDTO dto){
        System.out.println("下单成功,"+ dto);
        return dto.getOrderId();
    }
}

@Data
public class OrderDTO {

    @JsonProperty("order-id")
    private String orderId;
    @JsonProperty("order-name")
    private String orderName;

    private String attr3;
    private String attr4;
    private String attr5;

    // 可以有N个参数
    @JsonProperty("order-n")
    private String attrN;
    
    private GoodsDTO goods;
}

@Data
public class GoodsDTO {

    @JsonProperty("goods-id")
    private String goodsId;
    @JsonProperty("goods-name")
    private String goodsName;

    @JsonProperty("goods-color")
    private String goodsColor;

    @JsonProperty("goods-attr3")
    private String goodsAttr3;
    @JsonProperty("goods-attr4")
    private String goodsAttr4;
    @JsonProperty("goods-attr5")
    private String goodsAttr5;
    @JsonProperty("goods-attr-n")
    private String goodsAttrN;

    private SpecDTO spec;
}

@Data
public class SpecDTO {

    @JsonProperty("spec-name")
    private String specName;

    @JsonProperty("spec-code")
    private String specCode;

    // 如高,宽等参数 弱类型
    private Map<String,Object> params;
}
  • 3、客户端代码
// 引入Openfeign依赖
<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-openfeign</artifactId>
     <version>2.1.3.RELEASE</version>
 </dependency>
 
 //定义服务端api调用客户端接口
 @FeignClient(name = "Order",url = "http://localhost:8123")
public interface OrderClientService {

    @PostMapping("/api/order/create")
    String createOrder(OrderDTO dto);
}

// 客户端调用代码
@RestController
@RequestMapping("api/test")
public class TestController {

    @Autowired
    OrderClientService clientService;

    @Autowired
    Environment environment;

    @PostMapping("/create")
    public String create(){

        String property = environment.getProperty("order-name");
        System.out.println(property);

        Properties allParam =(Properties) ((StandardServletEnvironment) environment).getPropertySources().get("order-all").getSource();
        Properties pageSubmitParam =(Properties) ((StandardServletEnvironment) environment).getPropertySources().get("order-default").getSource();
        ParamUtils.updateParam(allParam,pageSubmitParam);

        OrderDTO dto = ParamUtils.toEntity(allParam,OrderDTO.class);
        String order = clientService.createOrder(dto);
        return "下单成功" + order;
    }
}

//通过配置文件实现全集默认 order-all.properties
order-id=order-01
order-name=购买手机
attr3=属性3
attr4=属性4
attr5=属性5
attr-n=属性5
goods.goods-id=goods-01
goods.goods-name=iphone
goods.goods-color=红色
goods.goods-attr3=goods-attr3
goods.goods-attr4=goods-attr4
goods.goods-attr5=goods-attr5
goods.goods-attr-n=goods-attr-n
goods.spec.spec-name=手机规格
goods.spec.spec-code=C003
goods.spec.params.height=10cm
goods.spec.params.weight=500g

//模拟页面提交的参数 order-default.properties
order-id=order-02
order-name=购买手机-iPhone13
goods.goods-id=goods-02
goods.goods-name=iphone13
goods.goods-color=黄色
goods.spec.spec-name=手机规格
goods.spec.spec-code=C004

// 核心工具类
@Component
@PropertySource(name = "order-all",value = {"order-all.properties"},encoding = "UTF-8")
@PropertySource(name = "order-default",value = {"order-default.properties"},encoding = "UTF-8")
public class ParamUtils {


    private static final ObjectMapper mapper;


    static{
        //创建ObjectMapper对象
        mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
        mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, true);
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModule(new JavaTimeModule());
    }

    /**
     * 页面提交的参数覆盖全集参数
     */
    public static void updateParam(Properties allParam, Properties pageSubmitParam){
        pageSubmitParam.forEach((key,value)->{
            if(StringUtils.hasLength(allParam.getProperty(key.toString()))){
                allParam.put(key,value);
            }
        });
    }

    /**
     * 嵌套对象字符goods.spec.params.height转换为嵌套对象,递归处理
     *
     * @param key  对象字符
     * @param value 值
     * @param parent 父对象
     */
    public static void formatChildObject(String key,Object value,Map<String,Object> parent){
        if(key.contains(".")){
            String currKey = key.split("\\.")[0];

            Map<String,Object> child = new HashMap<>();
            if(parent.containsKey(currKey)){
                Object oldChild = parent.get(currKey);
                if(!(oldChild instanceof Map)){
                    throw new RuntimeException(currKey+"属性存在多个,不能重复,转换失败!");
                }
                child = (Map<String,Object>)oldChild;
            }
            String nextKey = key.substring( key.indexOf(".")+1 );
            formatChildObject(nextKey,value,child);

        }else{
            parent.put(key,value);
        }

    }

    /**
     * 将参数转换为对象dto
     *
     * @param allParam api参数
     * @param clazz api参数模型
     * @param <T> api参数模型
     * @return api参数
     */
    public static <T> T toEntity(Properties allParam,Class<T> clazz){
        Map<String,Object> newMap = new HashMap<>();
        allParam.forEach((key,value)->{
            formatChildObject(key.toString(),value,newMap);
        });

        try {
            String str = mapper.writeValueAsString(newMap);
            return mapper.readValue(str,clazz);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }

}

四、总结

1.嵌套对象不支持嵌套列表和数组
2.可以通过JSR303校验实现参数格式的有效性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
大疆接口API是指大疆公司所提供的一组程序接口,用于与大疆无人机及其相关设备进行通信和控制。系统调用大疆接口API时,通常需要经过以下步骤: 1. 获取API密钥:首先,需要在大疆开发者平台注册账号,并创建一个应用程序,获取到API密钥。API密钥是用于标识和验证系统接口的访问权限的。 2. 导入SDK:系统需要下载并导入大疆官方提供的SDK(软件开发工具包),以便能够在系统代码中使用大疆接口API。SDK包含了各种功能模块和代码示例,可以快速集成到系统中。 3. 调用接口方法:在系统的代码中,可以根据需要选择合适的接口方法进行调用。大疆接口API提供了丰富的功能,包括无人机的控制、状态监测、飞行参数设置、航拍数据获取等等。 4. 传递参数:在调用接口方法时,根据方法的要求传递相应的参数。例如,若要控制无人机起飞,可能需要传递起飞高度、起飞速度等参数。 5. 处理返回结果:大疆接口API调用完成后,会返回相应的结果。系统需要根据返回结果进行处理,以便获取所需的信息或判断操作是否成功。 需要注意的是,在调用大疆接口API之前,系统要确保已经获得了控制无人机的相应权限,并了解大疆接口API的使用文档和规范,以便正确使用和处理接口。此外,系统也应该遵守相关法律法规以及安全操作要求,确保无人机的飞行安全和数据保密。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值