跳转至

脚本开发 / 基本概念

DataFlux Func 中,存在一些 DataFlux Func 中特有的概念,本文档将对此进行说明。

1. 脚本集、脚本和函数

脚本集、脚本和函数可在「开发 / 脚本库」中创建,是 DataFlux Func 的核心概念,ID 在用户创建 / 编写代码时直接指定。

  • 「脚本集」为数个脚本的集合,ID 在创建时由用户直接指定,且只能包含脚本。
  • 「脚本」即 Python 脚本本身,必然属于某一个脚本集,ID 在创建时由用户直接指定。
  • 「函数」在 DataFlux Func 中特指被 @DFF.API(...) 装饰器装饰的最顶层函数,可以被授权链接、自动触发配置、批处理等其他模块调用的入口函数。

脚本集与文件夹类似,但这个“文件夹”与一般 Python 编码中的文件夹无关

在 DataFlux Func 中进行编码时,会大量涉及到脚本集、脚本和函数的 ID,并且这些 ID 之间存在紧密的联系。

脚本集、脚本和函数 ID 之间关系

按照脚本集、脚本和函数层级关系,下层概念的 ID 一定包含了上层概念的 ID。

假设存在一个 ID 为demo的脚本集,那么属于此脚本集的所有脚本必然以demo__(双下划线)开头。

再假设此脚本集下有 ID 为demo__test的脚本,它包含一个函数def hello(...),那么这个函数的 ID 即为demo__test.hello

ID 示例表如下:

概念 ID 示例
脚本集 demo
脚本 demo__test
函数 demo__test.hello

编码中相互引用

在 DataFlux Func 的脚本中,允许引用另一脚本来实现代码复用。

假设存在脚本demo__script_a,其包含一个函数func_a()。那么,在脚本demo__script_b中引用此函数时,可以使用如下方法:

demo__script_a
1
2
def func_a():
    pass
demo__script_b
1
2
3
4
import demo__script_b

def test():
    return demo__script_b.func_a()

Python 的as语句也同样可以使用:

demo__script_b
1
2
3
4
import demo__script_b as b

def test():
    return b.func_a()

也可以使用from ... import语句只导入所需函数:

demo__script_b
1
2
3
4
from demo__script_b import func_a

def test():
    return func_a()

对于属于同一个脚本集的脚本之间引用,可以忽略脚本集 ID,以__(双下划线)开头的缩略形式表示:

demo__script_b
1
2
3
4
from __script_b import func_a

def test():
    return func_a()

尽可能使用缩略形式

在脚本集内部相互引用应当尽量使用缩略形式(即忽略脚本集 ID 并以__开头的形式)。

这样,在将整个脚本集克隆,脚本集 ID 发生改变后,克隆出来的新脚本集内的代码依然可以正确引用本脚本集内的脚本。

2. 连接器

连接器可在「开发 / 连接器」中创建,是由 DataFlux Func 提供的连接外部系统的工具,ID 在创建时由用户直接指定。

实际上,在 DataFlux Func 编写 Python 代码与原版 Python 并无太大区别。开发者完全可以忽略连接器,自行在代码中连接外部系统。

但对于一些存在连接池概念的外部系统来说,连接器内置了连接池,可在函数反复运行的过程中保持链接,避免反复创建 / 关闭与外部系统的连接。

假设用户已经配置好了一个 ID 为 mysql 的连接器,那么获取这个连接器的操作对象代码如下:

Python
1
mysql = DFF.CONN('mysql')

具体不同的连接器具有不同的操作方法和参数,详情请参考 脚本开发 / 连接器对象 DFF.CONN

3. 环境变量

环境变量可在「开发 / 环境变量」中创建,是由 DataFlux Func 提供的简单 Key-Value 配置读取工具,ID 在创建时由用户直接指定。

环境变量特别适合用于同一套代码运行在不同环境下的场景。

如脚本需要访问的系统区分了测试 / 生产环境,那么可以通过设置环境变量,实现在不改变代码的情况下,切换测试 / 生产环境。

假设用户已经配置好了一个 ID 为 api_endpoint 的环境变量,那么获取这个环境变量值的代码如下:

Python
1
api_endpoint = DFF.ENV('api_endpoint')

4. 授权链接

授权链接可在「管理 / 授权链接」中创建,是外部调用 DataFlux Func 中函数的一种常见方式,调用过程同步执行,函数执行完成后可以将结果直接返回给调用方。

为函数创建授权链接后,支持多种不同的调用方式。 授权链接支持GETPOST两种方式。两种不同方式的参数传递同时支持「简化形式」、「标准形式」。

此外,POST方式的「简化」形式还支持文件上传,以下是各种调用方式的功能支持列表:

调用方式 传递kwargs参数 kwargs参数类型 传递options 文件上传 提交任意格式的Body
1.6.9以后可用
GET简化形式 支持 仅限字符串 不支持 不支持 不支持
GET标准形式 支持 JSON 中的数据类型 支持 不支持 不支持
POST简化形式 支持 仅限字符串 不支持 支持 支持
POST标准形式 支持 JSON 中的数据类型 支持 不支持 不支持

对于 kwargs 中参数只能传递字符串的调用方式,需要在函数中对参数进行类型转换。在授权链接列表,可以点击「API 调用示例」来查看具体调用方式

假设存在如下函数:

Python
1
2
3
@DFF.API('我的函数')
def my_func(x, y):
    pass

为此函数创建的授权链接 ID 为auln-xxxxx,传递的参数为x = 100(整数), y = "hello"(字符串)。

那么,各种不同的调用方式如下:

GET 简化形式传参

如果函数的参数比较简单,可以使用GET简化形式传递参数,接口将更加直观。

由于 URL 中传递参数时,无法区分字符串的"100"和整数的100, 所以函数在被调用时,接收到的参数都是字符串。 函数需要自行对参数进行类型转换。

Text Only
1
GET /api/v1/al/auln-xxxxx/simplified?x=100&y=hello

为了便于阅读,示例为 URLEncode 之前的内容,实际 URL 参数需要进行 URLEncode

GET 标准形式传参

在某些情况下,如果无法发送 POST 请求,也可以使用 GET 方式调用接口。

GET标准形式传参时,将整个kwargs进行 JSON 序列化后,作为 URL 参数传递即可。 由于参数实际还是以 JSON 格式发送,因此参数的原始类型都会保留。 函数无需再对参数进行类型转换。

如本例中,函数接收到的x参数即为整数,无需类型转换。

Text Only
1
GET /api/v1/al/auln-xxxxx?kwargs={"x":100,"y":"hello"}

为了便于阅读,示例为 URLEncode 之前的内容,实际 URL 参数需要进行 URLEncode

POST 简化形式传参

在某些情况下,如果无法发送请求体为 JSON 的 HTTP 请求, 那么也可以类似 Form 表单的形式传递参数,各字段名即为参数名。

由于 Form 表单提交数据时,无法区分字符串的"100"和整数的100, 所以函数在被调用时,接收到的参数都是字符串。 函数需要自行对参数进行类型转换。

Text Only
1
2
3
4
POST /api/v1/al/auln-xxxxx/simplified
Content-Type: x-www-form-urlencoded

x=100&y=hello

此外,POST简化形式传参还额外支持文件上传(参数/字段名必须为files), 需要使用form-data/multipart方式进行处理。

页面HTML代码示例如下:

HTML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<html>
    <body>
        <h1>文件上传</h1>
        <input id="file" type="file" name="files" required />
        <input id="submit" type="submit" value="上传"/>
    </body>
    <script>
        // 授权链接地址(如本页面与 DataFlux Func 不在同一个域名下,需要写全 http://域名:端口/api/v1/al/auln-xxxxx/simplified
        // 注意:上传文件必须使用简化形式授权链接
        var AUTH_LINK_URL = '/api/v1/al/auln-xxxxx/simplified';

        document.querySelector('#submit').addEventListener('click', function(event) {
            // 点击上传按钮后,生成 FormData 对象后作为请求体发送请求
            var data = new FormData();
            data.append('x', '100');
            data.append('y', 'hello');
            data.append('files', document.querySelector('#file').files[0]);

            var xhr = new XMLHttpRequest();
            xhr.open('POST', AUTH_LINK_URL);
            xhr.send(data);
        });
    </script>
</html>

POST 标准形式传参

POST标准形式传参是最常见的调用方式。 由于参数以 JSON 格式通过请求体发送,因此参数的原始类型都会保留。 函数无需再对参数进行类型转换。

如本例中,函数接收到的x参数即为整数,无需类型转换。

Text Only
1
2
3
4
5
6
7
8
9
POST /api/v1/al/auln-xxxxx
Content-Type: application/json

{
    "kwargs": {
        "x": 100,
        "y": "hello"
    }
}

5. 自动触发配置

自动触发配置可在「管理 / 自动触发配置」中创建,用于让 DataFlux Func 定期自动调用函数。

为函数创建自动触发配置后,函数则会所指定的 Crontab 表达式定时执行,不需要外部进行调用。

正因为如此,所执行的函数的所有参数必须都已满足,即:

  1. 函数不需要输入参数
  2. 函数需要输入参数,但都是可选参数
  3. 函数需要必选参数,并在自动触发配置中为其配置具体的值

区分函数运行时所属执行功能

如果函数同时配置了「自动触发配置」和其他执行功能,并且希望在不同的执行功能中进行区分处理,可以判断内置变量_DFF_CRONTAB区分:

Python
1
2
3
4
5
6
7
8
9
@DFF.API('我的函数')
def my_func(x, y):
    result = x + y

    if _DFF_CRONTAB:
        # 只在自动触发配置时输出日志
        print(f'x + y = {result}')

    return

6. 批处理

批处理可在「管理 / 批处理」中创建,是外部调用 DataFlux Func 中函数的另一种方式。

与「授权链接」的区别在于,批处理被调用后会立刻返回,后台异步执行处理,调用方无需等待结果返回。除此之外,调用方式与「授权链接」相同。

批处理提供了更长的函数执行时间(非 API 响应时间),非常适合执行需要耗时较长的异步处理。

7. API 认证

此功能于 1.3.2 版本新增

批处理可在「管理 / 批处理」中创建,是外部调用 DataFlux Func 中函数的另一种方式。

对于「授权链接」和「批处理」所生成的 HTTP API,可以额外添加接口认证。

目前支持的接口认证如下:

认证类型 说明
固定字段 验证请求的 Header, Query 或 Body 中必须包含具有特定值的字段
HTTP Basic 标准 HTTP Basic 认证(在浏览器中访问可弹出登录框)
HTTP Digest 标准 HTTP Digest 认证(在浏览器中访问可弹出登录框)
认证函数 指定自行编写的函数作为认证函数

用户可以在「管理 / API 认证」添加认证配置,随后在「授权链接/批处理 配置」中指定所添加的认证配置。

如对安全性有较高要求,请务必使用 HTTPS 方式访问接口

固定字段认证

固定字段认证是最简单的认证方式,即客户端与 DataFlux Func 约定在请求的某处(Header、Query 或 Body)包含一个特定的字段和字段值,在每次调用时附带此内容以完成认证。

假设约定每次请求中,请求头必须包含x-auth-token="my-auth-token",那么按照以下方式调用即可完成认证:

Text Only
1
2
GET /api/v1/al/auln-xxxxx
x-auth-token: my-auth-token

配置多个固定字段认证时,有一个匹配即认为通过认证

对于 Query 和 Body 中用于认证的字段,认证通过后系统会自动将其删除,不会传递到函数

HTTP Basic / HTTP Digest

浏览器直接支持的认证方式。

使用此方式认证的接口,在浏览器地址栏中直接访问时,浏览器会弹出用户名/密码框供用户填写。

如需要使用编程方式访问,请参考如下代码:

Python
1
2
3
4
5
6
7
8
import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth

# HTTP Basic 认证
resp = requests.get(url_1, auth=HTTPBasicAuth('user', 'password'))

# HTTP Digest 认证
resp = requests.get(url_2, auth=HTTPDigestAuth('user', 'password'))

认证函数

如果接口认证方式复杂或特殊(如需要对接业务系统等),可以选择自行编写函数方式认证。

用于认证的函数必须满足「有且只有一个req参数,作为请求」,返回TrueFalse表示认证成功或失败。

参数req是一个dict,具体结构如下:

字段名 字段值类型 说明
method str 请求方法(大写)
如:"GET""POST"
originalUrl str 请求原始 URL。包含?后的部分
如:/api/v1/al/auln-xxxxx?q=1
url str 请求 URL。不包含?后的部分
如:/api/v1/al/auln-xxxxx
headers dict 请求 Header,字段名均为小写
query dict 请求 Query,字段名和字段值都为字符串
body dict 请求 Body
hostname str 请求访问的主机名。不包含端口号部分
如:example.com
ip str 客户端 IP
注意:此字段需要 Nginx、阿里云 SLB 等正确配置才有意义
ips list 客户端 IP 及所有中间代理服务器 IP 地址列表
注意:此字段需要 Nginx、阿里云 SLB 等正确配置才有意义
ips[#] str 中间代理服务器 IP
xhr bool 是否为 ajax 请求

一个简单的示例如下:

Python
1
2
3
@DFF.API('认证函数')
def my_auth_func(req):
    return req['headers']['x-auth-token'] == 'my-auth-token'