Skip to content

外部 API

NetNexus 外部 API 是本机只读 HTTP 服务。当前只注册 BMP 查询接口。

外部 API 不负责启动 BMP。BMP 仍由 BMP 页面启动,API 只查询当前 worker 内存中的实时数据。

服务配置

在设置窗口进入「API」。

配置项默认值范围/格式说明
启用API服务falsetrue / false保存后立即启动或停止外部 API 服务
监听地址127.0.0.1固定值仅允许本机访问,不支持 0.0.0.0 等全局监听
监听端口18080整数 1..65535API HTTP 服务端口
分页最大条数1000整数 1..10000限制路由分页接口的 pageSize 最大值

本地 mock 数据

如需在没有真实 BMP 对接的情况下验证页面布局或 API 返回,可以先在 BMP 页面启动服务,再运行:

bash
npm run mock:bmp

常用参数:

bash
node scripts/mockBmpClient.js --host 127.0.0.1 --port 11019 --routes 25 --interval 30 --once

mock 脚本发送 BMPv4 draft-20 TLV 格式。BMP 页面里的 v4 TLV格式 需要与之保持一致,否则 Route Monitoring 可能无法解析出 BGP Message TLV

会话 Statistics Report 会在同一帧中上报 Pre/Post Adj-RIB-In/Out 四个阶段。当 --routes=N 时,四类全局统计值分别为 N / max(0,N-1) / N+2 / N+1,并同时携带 IPv4 Unicast 的 per-AFI/SAFI 统计。Loc-RIB Statistics Report 覆盖 RD 0:065000:10065000:12065000:102;其中 65000:102 同时上报 IPv4 Unicast 与 IPv4 Labeled Unicast。

通用规则

项目规则
协议HTTP
监听地址固定 127.0.0.1,不允许全局监听
数据格式JSON
GET 请求体忽略
POST 请求头建议 Content-Type: application/json
请求体大小最大 64KB
字符串处理trim;控制字符会被拒绝
未启动 BMPBMP 查询接口返回 HTTP 409,错误码 BMP_NOT_RUNNING

响应格式

成功响应:

字段类型说明
statusstring固定为 success
msgstring描述信息
dataany接口数据
json
{
  "status": "success",
  "msg": "获取BMP状态成功",
  "data": {
    "running": true
  }
}

错误响应:

字段类型说明
statusstring固定为 error
codestring机器可读错误码
msgstring错误描述
dataany附加错误数据,通常为 null
json
{
  "status": "error",
  "code": "INVALID_PARAMETER",
  "msg": "pageSize必须是1到1000之间的整数",
  "data": null
}

错误码

HTTP 状态码code说明
400INVALID_JSON请求体不是合法 JSON
400INVALID_PARAMETER参数校验失败
400BMP_QUERY_FAILEDBMP 查询失败
404ROUTE_NOT_FOUND接口不存在
405METHOD_NOT_ALLOWED请求方法不支持
409BMP_NOT_RUNNINGBMP 未启动
413REQUEST_TOO_LARGE请求体超过 64KB
500INTERNAL_ERROR服务内部错误

通用对象

下面的对象分为两种用途:

  • 请求最小字段:接口实际校验和读取的字段,只要传这些字段即可。
  • 返回扩展字段:查询接口返回的完整对象字段,可以原样传给后续接口;多余字段会被忽略。

Client 对象

client 建议直接使用 GET /api/v1/bmp/clients 返回对象中的连接字段。

字段类型必填范围/格式说明
localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
localPortinteger0..65535NetNexus 本地监听端口
remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
remotePortinteger0..65535BMP 客户端端口

最小请求示例:

json
{
  "client": {
    "localIp": "127.0.0.1",
    "localPort": 11019,
    "remoteIp": "127.0.0.1",
    "remotePort": 50000
  }
}

Session 对象

session 建议直接使用 POST /api/v1/bmp/sessions 返回对象中的关键字段。

字段类型必填范围/格式说明
sessionTypeinteger0..255BMP peer type;常见 0 Global、1 L3VPN、2 Local
sessionRdstring1..128 字符,不能包含控制字符RD
sessionIpstring1..128 字符,不能包含控制字符被监控 BGP peer 地址
sessionAsinteger0..4294967295被监控 BGP peer AS

最小请求示例:

json
{
  "session": {
    "sessionType": 0,
    "sessionRd": "0:0",
    "sessionIp": "192.0.2.1",
    "sessionAs": 65001
  }
}

Instance 对象

instance 建议直接使用 POST /api/v1/bmp/instances 返回对象中的关键字段。

字段类型必填范围/格式说明
instanceTypeinteger0..255BMP instance peer type;Loc-RIB 常见为 3
instanceRdstring1..128 字符,不能包含控制字符RD
addrFamilyTypeinteger见地址族枚举Loc-RIB instance 地址族

最小请求示例:

json
{
  "instance": {
    "instanceType": 3,
    "instanceRd": "0:0",
    "addrFamilyType": 1
  }
}

分页过滤参数

字段类型必填默认值范围/格式说明
pageinteger11..1000000页码,从 1 开始
pageSizeinteger201..设置页分页最大条数每页条数
routeStatestringactiveactivestaleall路由状态过滤
prefixFilterstring最大 128 字符,不能包含控制字符Prefix 或 Prefix/Mask 过滤

地址族枚举

名称
1IPv4 UNC
2IPv6 UNC
3L2VPN EVPN
4VPNV4
5VPNV6
6IPv4 MVPN
7IPv6 MVPN
8IPv4 QP
9IPv6 QP
10IPv4 FlowSpec
11IPv6 FlowSpec
12IPv4 Label
13IPv6 Label
14Link-State
15Link-State VPN

RIB 类型枚举

名称
1Pre Adj RIB In
2Adj RIB In
3AS Path
4Adj RIB Out
5Post Adj RIB Out

Route List Item

路由列表接口的 data.list[] 元素。

字段类型说明
routeKeystring路由详情查询 key,opaque 字符串,必须原样使用列表接口返回值
addrFamilyTypeinteger/null地址族枚举值
afiinteger/nullBGP AFI
safiinteger/nullBGP SAFI
ipstring/null路由前缀
maskinteger/null掩码长度
rdstring/nullRD
originstring/number/nullOrigin 属性
asPathstring/nullAS Path
mednumberMED
nextHopstring/nullNext Hop
pathIdnumber/string/nullADD-PATH path id
labelsstring/nullMPLS label 文本
parserValidbooleanNLRI 解析是否有效
parseErrorsstring/null解析错误
parseWarningsstring/null解析警告
pathStatusnumber/nullBMPv4 Path Marking 状态位
pathStatusNamesarray已识别 Path Status 名称
pathStatusTextstring/nullPath Status 文本
pathStatusUnknownBitsnumber未识别 Path Status 位
pathStatusReasonnumber/nullPath Status reason code
pathStatusReasonNamestring/nullreason 名称
pathStatusReasonTextstring/nullreason 文本
routeStatestringactivestale

routeKey 只用于精确查询,不支持前缀匹配。不要按 pathId|rd|ip|mask 自行解析或拼接;IPv4/IPv6/VPN/EVPN 等地址族都会把真实 NLRI 展示文本放进同一个 key 中,EVPN 可能出现 evpn:mac-ip:...evpn:ip-prefix:...evpn:leaf-ad:... 这类可变字符串。调用详情接口时应直接传列表返回的完整 routeKey

Route Detail

路由详情接口返回 Route List Item 的全部字段,并额外包含:

字段类型说明
localPrefnumberLocal Preference
communitiesstring/nullCommunity 文本
otcnumber/string/nullOTC 属性
routeTypestring/null路由类型,依地址族而定
rawNlristring/null原始 NLRI
nlriDetailobject/nullNLRI 解析详情
pathStatusReasonsarrayPath Marking reason 列表
pathStatusTlvsarrayPath Marking TLV 解析结果
ribEpochnumberRIB 刷新 epoch
staleEpochnumber/null进入 stale 的 epoch
lastSeenAtstring/null最后一次看到该路由的 ISO 时间
staleAtstring/null标记 stale 的 ISO 时间
staleReasonstring/nullstale 原因
summarystring/nullBGP 报文解析摘要

Statistics Item

统计报表中的 statistics[] 元素。

字段类型说明
typeintegerBMP statistics type
valuenumber/string统计值;超过 JS 安全整数时返回字符串
valueHexstring原始统计值十六进制
afiinteger/null按 AFI/SAFI 统计时有值
safiinteger/null按 AFI/SAFI 统计时有值
typeNamestring统计类型名称

TLV Item

多处返回中的 rawTlvspeerUpTlvstlvs 等 TLV 数组元素。

字段类型说明
typeintegerTLV type,去除 enterprise bit 后的值
rawTypeinteger原始 TLV type
lengthintegerTLV length
enterpriseboolean是否为 enterprise TLV
enterpriseNumberinteger/nullenterprise number
valueHexstringvalue 十六进制
rawValueHexstringraw value 十六进制
indexintegerBMPv4 indexed TLV 时存在
rawIndexintegerBMPv4 indexed TLV 时存在
groupbooleanBMPv4 indexed TLV 时存在
namestring已识别名称,可能不存在
valuestring文本 TLV 解码值,可能不存在
decodedobject结构化解码结果,可能不存在

接口清单

GET /api/v1/status

查询外部 API 服务状态和已注册模块。

请求参数:无。

返回 data

字段类型说明
runningbooleanAPI server 是否运行
enabledboolean设置中是否启用
hoststring当前监听地址
portinteger当前监听端口
modulesstring[]已注册模块,目前包含 bmp

GET /api/v1/bmp/status

查询 BMP 是否启动。

请求参数:无。

返回 data

字段类型说明
runningbooleanBMP worker 是否运行

GET /api/v1/bmp/clients

查询 BMP 客户端列表。

请求参数:无。

返回 data:Client 扩展对象数组。

字段类型说明
localIpstringNetNexus 本地监听地址
localPortintegerNetNexus 本地监听端口
remoteIpstringBMP 客户端地址
remotePortintegerBMP 客户端端口
sysNamestring/nullInitiation TLV sysName
sysDescstring/nullInitiation TLV sysDesc
bmpVersioninteger/nullBMP 版本
bmpV4TlvDraftintegerBMPv4 TLV draft,1920
rawTlvsTLV Item[]Initiation TLV
terminationTlvsTLV Item[]Termination TLV
receivedAtstring/nullInitiation 接收时间

示例:

bash
curl http://127.0.0.1:18080/api/v1/bmp/clients

POST /api/v1/bmp/sessions

查询指定 BMP 客户端下的 BGP session。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口

返回 data:Session 扩展对象数组。

字段类型说明
sessionTypeintegerBMP peer type
sessionFlagsinteger/null生效后的 peer flags
rawSessionFlagsinteger/null原始 peer flags
sessionRdstringRD
sessionIpstring被监控 BGP peer 地址
sessionAsinteger被监控 BGP peer AS
sessionRouterIdstring/nullRouter ID
sessionTimestampinteger/nullBMP peer header timestamp 秒
sessionTimestampMsinteger/nullBMP peer header timestamp 微秒
localIpstring/null被监控 BGP session 本地地址
localPortinteger/null被监控 BGP session 本地端口
remotePortinteger/null被监控 BGP session 远端端口
sessionStateinteger/null0 Peer Up,1 Peer Down
recvAddressFamiliesarray收到的能力地址族
sendAddressFamiliesarray发送的能力地址族
enabledAddressFamiliesarray双方共同启用的地址族
enabledAddrFamilyTypesinteger[]地址族枚举值
ribTypesinteger[]支持的 RIB 类型枚举
recvAddPathMapobject收到的 ADD-PATH 能力
sendAddPathMapobject发送的 ADD-PATH 能力
addPathReceiveMapobjectRouter receive ADD-PATH 状态
addPathSendMapobjectRouter send ADD-PATH 状态
addPathMapobject地址族维度 ADD-PATH 是否启用
peerUpTlvsTLV Item[]Peer Up TLV
peerDownTlvsTLV Item[]Peer Down TLV
peerDownReasoninteger/nullPeer Down reason
peerDownFsmEventCodeinteger/nullFSM event code
ribEpochMapobjectRIB epoch
routeSummaryobject{ active, stale, total }

示例:

json
{
  "client": {
    "localIp": "127.0.0.1",
    "localPort": 11019,
    "remoteIp": "127.0.0.1",
    "remotePort": 50000
  }
}

POST /api/v1/bmp/instances

查询指定 BMP 客户端下的 Loc-RIB instance。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口

返回 data:Instance 扩展对象数组。

字段类型说明
addrFamilyTypeinteger地址族枚举值
instanceTypeintegerBMP instance peer type
instanceFlagsinteger/null生效后的 flags
rawInstanceFlagsinteger/null原始 flags
instanceRdstringRD
instanceIpstring/nullinstance 地址
instanceAsinteger/nullinstance AS
instanceRouterIdstring/nullRouter ID
instanceTimestampinteger/nullBMP peer header timestamp 秒
instanceTimestampMsinteger/nullBMP peer header timestamp 微秒
localIpstring/null本地地址
localPortinteger/null本地端口
remotePortinteger/null远端端口
instanceStateinteger/null0 Peer Up,1 Peer Down
recvAddressFamiliesarray收到的能力地址族
sendAddressFamiliesarray发送的能力地址族
enabledAddressFamiliesarray双方共同启用的地址族
enabledAddrFamilyTypesinteger[]地址族枚举值
ribTypesinteger[]支持的 RIB 类型枚举
recvAddPathMapobject收到的 ADD-PATH 能力
sendAddPathMapobject发送的 ADD-PATH 能力
addPathReceiveMapobjectRouter receive ADD-PATH 状态
addPathSendMapobjectRouter send ADD-PATH 状态
isAddPathboolean是否启用 ADD-PATH
peerUpTlvsTLV Item[]Peer Up TLV
vrfTableNamesstring[]VRF table name
ribEpochintegerRIB epoch
routeSummaryobject{ active, stale, total }

POST /api/v1/bmp/routes

分页查询 session RIB 路由。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口
session.sessionTypeinteger0..255BMP peer type
session.sessionRdstring1..128 字符,不能包含控制字符RD
session.sessionIpstring1..128 字符,不能包含控制字符被监控 BGP peer 地址
session.sessionAsinteger0..4294967295被监控 BGP peer AS
afinteger地址族枚举 1..15查询地址族
ribTypeintegerRIB 类型枚举 1..5查询 RIB
pageinteger1..1000000页码,默认 1
pageSizeinteger1..设置页分页最大条数每页条数,默认 20
routeStatestringactivestaleall路由状态过滤,默认 active
prefixFilterstring最大 128 字符,不能包含控制字符Prefix 或 Prefix/Mask 过滤,默认空

返回 data

字段类型说明
listRoute List Item[]当前页路由列表
totalinteger满足过滤条件的总数
summaryobject当前 RIB 的 { active, stale, total }

示例:

json
{
  "client": {
    "localIp": "127.0.0.1",
    "localPort": 11019,
    "remoteIp": "127.0.0.1",
    "remotePort": 50000
  },
  "session": {
    "sessionType": 0,
    "sessionRd": "0:0",
    "sessionIp": "192.0.2.1",
    "sessionAs": 65001
  },
  "af": 1,
  "ribType": 2,
  "page": 1,
  "pageSize": 20,
  "routeState": "all",
  "prefixFilter": "10.0.0.0/24"
}

POST /api/v1/bmp/routes/detail

查询 session RIB 路由详情。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口
session.sessionTypeinteger0..255BMP peer type
session.sessionRdstring1..128 字符,不能包含控制字符RD
session.sessionIpstring1..128 字符,不能包含控制字符被监控 BGP peer 地址
session.sessionAsinteger0..4294967295被监控 BGP peer AS
afinteger地址族枚举 1..15查询地址族
ribTypeintegerRIB 类型枚举 1..5查询 RIB
routeKeystring1..2048 字符,不能包含控制字符路由列表返回的完整 routeKey;opaque 字符串,精确匹配

返回 data:Route Detail 对象;路由不存在时返回错误。路由 NLRI 解析结果在 nlriDetail 字段中。

POST /api/v1/bmp/instances/routes

分页查询 Loc-RIB instance 路由。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口
instance.instanceTypeinteger0..255BMP instance peer type
instance.instanceRdstring1..128 字符,不能包含控制字符RD
instance.addrFamilyTypeinteger地址族枚举 1..15Loc-RIB instance 地址族
pageinteger1..1000000页码,默认 1
pageSizeinteger1..设置页分页最大条数每页条数,默认 20
routeStatestringactivestaleall路由状态过滤,默认 active
prefixFilterstring最大 128 字符,不能包含控制字符Prefix 或 Prefix/Mask 过滤,默认空

返回 data

字段类型说明
listRoute List Item[]当前页路由列表
totalinteger满足过滤条件的总数
summaryobject当前 instance 的 { active, stale, total }

示例:

json
{
  "client": {
    "localIp": "127.0.0.1",
    "localPort": 11019,
    "remoteIp": "127.0.0.1",
    "remotePort": 50000
  },
  "instance": {
    "instanceType": 3,
    "instanceRd": "0:0",
    "addrFamilyType": 1
  },
  "page": 1,
  "pageSize": 20,
  "routeState": "all",
  "prefixFilter": "10.0.0.0/24"
}

POST /api/v1/bmp/instances/routes/detail

查询 Loc-RIB instance 路由详情。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口
instance.instanceTypeinteger0..255BMP instance peer type
instance.instanceRdstring1..128 字符,不能包含控制字符RD
instance.addrFamilyTypeinteger地址族枚举 1..15Loc-RIB instance 地址族
routeKeystring1..2048 字符,不能包含控制字符路由列表返回的完整 routeKey;opaque 字符串,精确匹配

返回 data:Route Detail 对象;路由不存在时返回错误。路由 NLRI 解析结果在 nlriDetail 字段中。

POST /api/v1/bmp/statistics/session

查询 session 统计报表。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口

返回 data:统计报表数组。

字段类型说明
clientClient 扩展对象BMP 客户端信息
sessionSession 扩展对象BGP session 信息
ribTypeintegerRIB 阶段:1 Pre Adj-RIB-In、2 Post Adj-RIB-In、4 Pre Adj-RIB-Out、5 Post Adj-RIB-Out
statisticsStatistics Item[]统计项
tlvsTLV Item[]BMPv4 Statistics Report TLV
updatedAtstring报表更新时间 ISO 字符串

POST /api/v1/bmp/statistics/instance

查询 Loc-RIB instance 统计报表。

请求参数(下表列出接口实际读取的参数字段;返回对象里的其它字段不是必填,可原样带上但会被忽略):

字段类型必填范围/格式说明
client.localIpstring1..128 字符,不能包含控制字符NetNexus 本地监听地址
client.localPortinteger0..65535NetNexus 本地监听端口
client.remoteIpstring1..128 字符,不能包含控制字符BMP 客户端地址
client.remotePortinteger0..65535BMP 客户端端口

返回 data:统计报表数组。

字段类型说明
clientClient 扩展对象BMP 客户端信息
instanceobjectLoc-RIB instance 信息
statisticsStatistics Item[]统计项
tlvsTLV Item[]BMPv4 Statistics Report TLV
updatedAtstring报表更新时间 ISO 字符串

instance 字段:

字段类型说明
instanceTypeintegerBMP instance peer type
instanceFlagsintegerflags
instanceRdstringRD
instanceIpstringinstance 地址
instanceAsintegerinstance AS
instanceRouterIdstringRouter ID
instanceTimestampintegertimestamp 秒
instanceTimestampMsintegertimestamp 微秒
vrfTableNamesstring[]VRF table name

推荐调用顺序

步骤接口说明
1GET /api/v1/bmp/status确认 BMP 已启动
2GET /api/v1/bmp/clients获取 client 标识
3POST /api/v1/bmp/sessions获取 session 标识
4POST /api/v1/bmp/routes分页查询 session 路由
5POST /api/v1/bmp/routes/detail使用 routeKey 查询详情

Loc-RIB 查询把第 3 到 5 步替换为:

步骤接口说明
3POST /api/v1/bmp/instances获取 instance 标识
4POST /api/v1/bmp/instances/routes分页查询 instance 路由
5POST /api/v1/bmp/instances/routes/detail使用 routeKey 查询详情

Python 调用指导

以下示例只使用 Python 标准库,无需安装第三方依赖。

变量说明
BASE_URL外部 API 地址,例如 http://127.0.0.1:18080
api_get(path)发起 GET 请求
api_post(path, payload)发起 POST JSON 请求
python
import json
import urllib.error
import urllib.request

BASE_URL = "http://127.0.0.1:18080"


def request_api(method, path, payload=None):
    data = None
    headers = {
        "Accept": "application/json",
    }
    if payload is not None:
        data = json.dumps(payload).encode("utf-8")
        headers["Content-Type"] = "application/json"

    req = urllib.request.Request(
        f"{BASE_URL}{path}",
        data=data,
        headers=headers,
        method=method,
    )

    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            body = json.loads(resp.read().decode("utf-8"))
            return resp.status, body
    except urllib.error.HTTPError as exc:
        body = json.loads(exc.read().decode("utf-8"))
        return exc.code, body


def api_get(path):
    return request_api("GET", path)


def api_post(path, payload):
    return request_api("POST", path, payload)


status_code, status = api_get("/api/v1/bmp/status")
print(status_code, status)
if status_code != 200 or not status["data"]["running"]:
    raise RuntimeError("BMP is not running")

_, clients = api_get("/api/v1/bmp/clients")
client = clients["data"][0]

_, sessions = api_post("/api/v1/bmp/sessions", {"client": client})
session = sessions["data"][0]

route_query = {
    "client": client,
    "session": session,
    "af": session["enabledAddrFamilyTypes"][0],
    "ribType": session["ribTypes"][0],
    "page": 1,
    "pageSize": 20,
    "routeState": "all",
    "prefixFilter": "",
}
_, routes = api_post("/api/v1/bmp/routes", route_query)
print(routes["data"]["total"], routes["data"]["list"])

if routes["data"]["list"]:
    route_key = routes["data"]["list"][0]["routeKey"]
    _, detail = api_post(
        "/api/v1/bmp/routes/detail",
        {
            **route_query,
            "routeKey": route_key,
        },
    )
    print(detail["data"])

Python 错误处理建议:

场景处理建议
HTTP 409 + BMP_NOT_RUNNING提示用户先在 NetNexus BMP 页面启动 BMP
HTTP 400 + INVALID_PARAMETER打印 msg,修正请求参数范围

Java 调用指导

以下示例使用 Java 11+ 标准库 java.net.http.HttpClient。JSON 字符串手工拼接只用于演示,生产代码建议使用 Jackson、Gson 等 JSON 库。

变量说明
baseUrl外部 API 地址,例如 http://127.0.0.1:18080
get(path)发起 GET 请求
post(path, json)发起 POST JSON 请求
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class NetNexusApiExample {
    private static final String baseUrl = "http://127.0.0.1:18080";
    private static final HttpClient client = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(10))
            .build();

    private static HttpResponse<String> get(String path) throws Exception {
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + path))
                .timeout(Duration.ofSeconds(10))
                .GET()
                .header("Accept", "application/json");
        return client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
    }

    private static HttpResponse<String> post(String path, String json) throws Exception {
        HttpRequest.Builder builder = HttpRequest.newBuilder()
                .uri(URI.create(baseUrl + path))
                .timeout(Duration.ofSeconds(10))
                .POST(HttpRequest.BodyPublishers.ofString(json))
                .header("Accept", "application/json")
                .header("Content-Type", "application/json");
        return client.send(builder.build(), HttpResponse.BodyHandlers.ofString());
    }

    public static void main(String[] args) throws Exception {
        HttpResponse<String> status = get("/api/v1/bmp/status");
        System.out.println("status code: " + status.statusCode());
        System.out.println(status.body());

        HttpResponse<String> clients = get("/api/v1/bmp/clients");
        System.out.println("clients code: " + clients.statusCode());
        System.out.println(clients.body());

        String sessionRequest = """
                {
                  "client": {
                    "localIp": "127.0.0.1",
                    "localPort": 11019,
                    "remoteIp": "127.0.0.1",
                    "remotePort": 50000
                  }
                }
                """;
        HttpResponse<String> sessions = post("/api/v1/bmp/sessions", sessionRequest);
        System.out.println("sessions code: " + sessions.statusCode());
        System.out.println(sessions.body());

        String routesRequest = """
                {
                  "client": {
                    "localIp": "127.0.0.1",
                    "localPort": 11019,
                    "remoteIp": "127.0.0.1",
                    "remotePort": 50000
                  },
                  "session": {
                    "sessionType": 0,
                    "sessionRd": "0:0",
                    "sessionIp": "192.0.2.1",
                    "sessionAs": 65001
                  },
                  "af": 1,
                  "ribType": 2,
                  "page": 1,
                  "pageSize": 20,
                  "routeState": "all",
                  "prefixFilter": ""
                }
                """;
        HttpResponse<String> routes = post("/api/v1/bmp/routes", routesRequest);
        System.out.println("routes code: " + routes.statusCode());
        System.out.println(routes.body());
    }
}

Java 集成建议:

场景建议
解析响应使用 Jackson/Gson 把响应反序列化为 ApiResponse<T>
超时控制HttpClient 和单个 HttpRequest 都设置 timeout
错误处理先判断 HTTP 状态码,再判断响应体 statuscode
client/session 获取不建议手写固定值,优先从 clientssessions 接口返回值中取

JavaScript 调用指导

浏览器或 Node.js 18+ 可以直接使用 fetch。Node.js 16 及以下需要安装 node-fetch 或使用内置 http 模块。

变量说明
baseUrl外部 API 地址,例如 http://127.0.0.1:18080
apiGet(path)发起 GET 请求
apiPost(path, payload)发起 POST JSON 请求
js
const baseUrl = 'http://127.0.0.1:18080';

async function requestApi(method, path, payload) {
    const headers = {
        Accept: 'application/json'
    };
    const options = {
        method,
        headers
    };
    if (payload !== undefined) {
        headers['Content-Type'] = 'application/json';
        options.body = JSON.stringify(payload);
    }

    const response = await fetch(`${baseUrl}${path}`, options);
    const body = await response.json();
    if (!response.ok || body.status !== 'success') {
        throw new Error(`${response.status} ${body.code || body.status}: ${body.msg || 'request failed'}`);
    }
    return body.data;
}

const apiGet = path => requestApi('GET', path);
const apiPost = (path, payload) => requestApi('POST', path, payload);

async function main() {
    const status = await apiGet('/api/v1/bmp/status');
    console.log('BMP status:', status);
    if (!status.running) {
        throw new Error('BMP is not running');
    }

    const clients = await apiGet('/api/v1/bmp/clients');
    const client = clients[0];

    const sessions = await apiPost('/api/v1/bmp/sessions', { client });
    const session = sessions[0];

    const routeQuery = {
        client,
        session,
        af: session.enabledAddrFamilyTypes[0],
        ribType: session.ribTypes[0],
        page: 1,
        pageSize: 20,
        routeState: 'all',
        prefixFilter: ''
    };
    const routes = await apiPost('/api/v1/bmp/routes', routeQuery);
    console.log('route total:', routes.total);
    console.log('route list:', routes.list);

    if (routes.list.length > 0) {
        const detail = await apiPost('/api/v1/bmp/routes/detail', {
            ...routeQuery,
            routeKey: routes.list[0].routeKey
        });
        console.log('route detail:', detail);
    }
}

main().catch(error => {
    console.error(error.message);
});

JavaScript 错误处理建议:

场景处理建议
409 BMP_NOT_RUNNING引导用户先启动 BMP
400 INVALID_PARAMETER直接展示响应体 msg,它会包含具体参数范围
空数组clientssessionsroutes.list 都可能为空,业务代码需要判空

基于 MIT License 发布