Skip to content

扩展库

概述

在开发过程中如果系统提供的功能不能满足要求,可以使用扩展库引入外部代码来实现,目前仅支持 java 代码的引入。需要将外部代码编译打包为zip文件。 调用informat.system.invokeLibrary方法就可以执行zip文件中的代码。

为了保证在调用过程中保持系统的无状态特性,只允许调用静态方法。并且强烈建议在代码中不要使用静态变量保存状态。

在扩展库中暴露的方法有以下几点限制

  • 方法名称必须保证唯一,不能通过返回值或者是参数类型进行方法重载
  • 方法的参数和返回值必须是可序列化的

在调用扩展库的时候会根据方法名称和参数数量进行方法匹配。如果找不到方法会抛出异常,如果匹配到的方法不唯一也会抛出异常。

扩展库中的方法

java
static int add(int a,int b);//方法1
static int add(int a,int b,int c);//方法2
static int add(String a,String b);//方法3

在脚本中调用

js
informat.system.invokeLibrary('mylibrary','com.mycompany.MyLibaray','add',[1,2,3])//匹配到方法2
informat.system.invokeLibrary('mylibrary','com.mycompany.MyLibaray','add',[1,2])//会抛出异常

zip 包说明

  • 将扩展库工程打包成一个名字为main.jar
  • 将工程依赖的 jar 包放入到lib目录(不支持多层级)下
  • 将目录使用 zip 无密码压缩
  • 将生成的 zip 包上传到应用设计器中的应用扩展

zip 包结构示例

.
├──lib
│   ├── lib1.jar
│   └── lib2.jar
└──main.jar

示例

以下是一个简单的扩展库,我们将其标识符定义为mylibrary

java
package com.mycompany;

import cn.hutool.crypto.digest.DigestUtil;

public class MyLibaray {
	public static int add(int a,int b) {
		return a+b;
	}
	public static String md5(String input) {//引用第三方库
        return DigestUtil.md5Hex(input);
    }
}

调用扩展库

java
var result=informat.system.invokeLibrary('mylibrary','com.mycompany.MyLibaray','add',[1,2]);
//result 为3
var result=informat.system.invokeLibrary('mylibrary','com.mycompany.MyLibaray','md5',['111111']);
//result 为3

INFO

demo下载地址:https://next.informat.cn/types/informat-next-library-demo.zip

工程打包mvn install后会在target目录下生成informat-next-library-demo-1.0.zip

应用设计->全局设计->高级设置->扩展库] 新增扩展库,上传informat-next-library-demo-1.0.zip文件

在扩展库中调用织信的接口

在扩展库中可以通过Informat对象调用织信提供的API。 调用织信API需要引入informat-spi.jar 下载地址为 https://next.informat.cn/types/informat-spi-1.0.0.jar

下面是一个调用织信提供的接口的示例

java
package com.mycompany;
public class MyLibaray {
	public static String getUser() {
		String userId=Informat.app().userId();//获取当前用户
        Informat.console().log("current userId is:"+userId);//将当前用户输出到应用控制台
        return userId;
	}
}

通过RPC方式调用Informat对象

默认情况下,扩展库需要部署到织信环境中才可以调用织信提供的方法,织信也提供了通过RPC远程调用织信提供的API。

INFO

使用远程方式调用API时,是上下文无关的,无法通过informat.app.userId()当前的操作用户。

通过RPC调用API时,调用客户端和织信服务器之间是通过HTTP请求的方式通讯,客户端会将参数序列化后发送给服务器,服务器将返回值序列化后返回给客户端。整个请求过程是基于request->response的方式进行,是无状态的。并且要求调用的函数参数和返回值必须是可序列化的,对于织信提供的某些API,例如informat.excel,informat.word等,在调用过程中会依赖一些上下文环境,并且参数或者是不可序列化的,在远程方式调用时会抛出异常。

使用RPC方式调用API需要设置以下参数

  • serverAddress 服务器的地址
  • appId APP的ID
  • apiKey APP的apikey

以下是通过HTTP方式调用的例子

java
package com.mycompany;
public class RemoteTest {
	public static void main(String args[]) {
        Informat.setServerAddress("https://next.informat.cn/web0");//织信API的调用地址
        Informat.setAppId("appId");//APP ID
        Informat.setApiKey("apiKey");//APP 的apikey
	    Map<String,Object>record=Informat.table().queryById("table","001");
        System.out.println("record is "+record);
	}
}

通过HTTP调用Informat对象

如果您使用其它语言开发,也可以通过HTTP POST 请求的方式调用织信API。请求方式如下

请求地址

https://next.informat.cn/web${cluster}/spi/invoke/${appId}?sign=${sign}
  • ${cluster}是集群部署的节点ID,默认为0,
  • ${appId} 是应用的ID
  • ${sign} 调用接口的签名

假设应用ID为app1,那么完整的地址如下

https://next.informat.cn/web0/spi/invoke/app1

请求参数

请求体结构为

ts
{
    method:string,//需要调用的方法,例如需要调用`console`的`log`方法,需要设置为`console.log`
    args:object[];,//参数列表,如果参数为空值要设置为null,例如['abc',null,123]
    timestamp:integer,//调用请求时的时间戳,需要确保客户端的时钟和服务器时钟的误差在15分钟之内,误差超过十五分钟的请求会被拒绝。
    nonce:string,//请求ID,在返回结果中会返回此ID,建议使用UUID来生成nonce,以保证每个请求的id独立
}

请求体body的内容需要设置成上面描述的对象生成的JSON,下面是一个请求体的例子

json
{
    "method":"console.log",
    "args":["arg1"],
    "timestamp":1670917594517,
    "nonce":"001"
}

请求签名sign

传递请求签名的目的为了

  • 校验请求者的合法性。
  • 校验参数的完整性和是否被篡改。
  • 防止重放攻击。

请求签名需要使用 SHA256withRSA 算法计算,计算方法为

js
let req //请求体
let json=JSON.stringify(req)//将请求体转换为JSON格式的字符串
let sign=SHA256withRSA(json,privateKey)//使用privateKey 计算签名,privateKey是应用的API Key

返回内容

服务器返回的结构如下

js
{
    nonce:String,//请求参数中的nonce
    ret:Object,//方法的返回值
    exceptionClass:String,//方法抛出的异常
    exceptionMessage:String//方法抛出的异常信息
}

服务器返回的内容为上述结构转换为JSON之后的字符串,Content-Typetext/json