Skip to content

2. 接入准备

2.1 服务地址与协议

说明
服务地址http://<设备地址>:8085端口来自 conf/app.confhttpport = 8085
协议HTTP实测真机 8085 在监听
路径前缀/v1所有接口均以该前缀开头
静态资源 / 前端页面GET /与 API 同源,前端是同一进程提供的页面
CORS全放开main.gocors.AllowAllowAllOrigins=trueAllowCredentials=true,且 ExposeHeadersSet-Cookie
未登录的响应HTTP 401 + HTML返回体是 <a href="/">Unauthorized</a>不是 JSON,详见 2.3

【警告】不要想当然认为有 HTTPS。仓库 conf/app.conf 里确实写了 EnableHTTPS = true / HttpsPort = 8010,也开了 beego 管理端 AdminAddr = "localhost" / AdminPort = 8088EnableAdmin = true),但实测真机上 80108088 均未监听,只有 8085 是开的。不同版本 / 不同交付配置可能不一样,上线前请自行确认实际端口,不要照抄配置文件。

端口探测(只读):

bash
for p in 8085 8010 8088; do
  (echo >/dev/tcp/<设备地>/$p) >/dev/null 2>&1 && echo "$p open" || echo "$p closed"
done

2.2 请求 / 响应公共约定

响应包装结构:DASS2 绝大多数接口由基类 BaseController 的两个方法输出,结构是固定的四字段:

字段类型说明
codeint0=成功-1=业务失败;1=部分查询类接口的「无数据」错误;其它值见 6-错误码
msgstring成功恒为 "ok";失败恒为 "error"
countint记录数 / 计数。含义随接口变化,不总是"列表长度"
data任意业务数据。失败时的真实错误文案放在这里,不在 msg
json
{ "code": 0, "count": 2, "data": [ ], "msg": "ok" }

源码(controllers/base.go):

go
// AjaxList → {"code","msg","count","data"}
func (self *BaseController) AjaxList(msg interface{}, msgCode int, count int64, data interface{})
// AjaxMsg → {"code","msg"}
func (self *BaseController) AjaxMsg(msg interface{}, msgno int)

【警告】msg 里永远只有 "ok""error",真正的错误原因在 data 里。 二次开发必须从 data 取错误文案,只打印 msg 会得到一句没用的 error

三个必须知道的坑

坑 1:code == 0 不代表成功。 部分接口在失败时把错误码传了 0,于是出现 code:0msg:"error" 并存。实测:

bash
# 查询一个不存在的采集服务配置
GET /v1/das/conf/99999
json
{"code":0,"count":0,"data":"DAS服务配置错误\u003cQuerySeter\u003e no row found","msg":"error"}

判定成功的正确姿势是 code == 0 && msg == "ok",或者干脆以 msg == "ok" 为准。

坑 2:错误码不统一。 同样是失败,不同接口给出的 code 不同:

场景实测响应
密码错误{"code":-1,"count":0,"data":"账号或密码错误","msg":"error"}
非 POST 调登录{"code":-1,"count":0,"data":"只允许POST方法","msg":"error"}
服务不存在(停止服务){"code":-1,"count":1,"data":"服务不存在\u003cQuerySeter\u003e no row found","msg":"error"}
服务配置不存在{"code":0,"count":0,"data":"DAS服务配置错误…","msg":"error"}
未登录(受保护接口)HTTP 401 + <a href="/">Unauthorized</a>
未登录调 /v1/isLogin/HTTP 302/
未登录调 /v1/logout/HTTP 500(详情见 7-已知限制与坑)

坑 3:返回值就是字符串,不是对象。 很多失败响应中 data 是一个纯文本字符串而非对象,例如 "data":"DAS服务配置错误..."。客户端若强类型反序列化成 struct 会直接失败。

Content-Type 约定

场景约定
默认application/json; charset=utf-8
登录application/x-www-form-urlencoded(表单)
建服务 / 改配置application/x-www-form-urlencoded同名字段可重复(表单字段即驱动配置项集合)
文件上传(测点导入、上传点导入)multipart/form-data
文件下载(测点导出、模板下载、日志下载)application/octet-stream,模板为 xlsx(实测响应体是 ZIP 头 PK

【注意】没有统一的 HTTP 状态码语义。业务失败时 HTTP 状态码通常仍是 200;只有鉴权失败(401/302)和个别异常(500)会不同。必须解析响应体,不能只看 HTTP 状态码。

2.3 鉴权

2.3.1 鉴权是怎么实现的

DASS2 的鉴权不在 main.go,而是在 models/timer/timer.go 里注册的一个全局过滤器:

go
beego.InsertFilter("/*", beego.BeforeRouter, filter.FilterBefore)   // models/timer/timer.go:78

过滤器本体在 models/filter/filter.go。它的判定顺序如下(照源码直译):

步骤条件结果
1配置 Base.RequireLoginLogin == true 请求来自 127.0.0.1 / localhost直接放行(免鉴权)
2URL 包含 login放行
3URL 等于 /放行
4URL 包含 socket.io放行
5URL 包含 logout放行
6URL 不包含 v1放行
7Session 中没有 user401isLogin 则 302)
8请求里没有以「用户名」为名的 Cookie,或该 Cookie 值不是 a|b|c 三段401isLogin 则 302)
9Cookie 第三段(过期时间戳)小于当前时间401isLogin 则 302)
10以上全部通过放行,并刷新 Cookie 的过期时间为「当前 + 24 小时」(滑动续期)

2.3.2 登录

方法与路径POST /v1/login(路由注册为 /v1/login/,beego 自动兼容无尾斜杠)
鉴权免认证(URL 含 login,命中步骤 2)
Content-Typeapplication/x-www-form-urlencoded
请求字段usernamepasswordmd5(明文) 的小写 hex,32 位
bash
# 计算密码 MD5(macOS)
PWD_MD5=$(md5 -q -s '<管理员明文密码>')
# 计算密码 MD5(Linux)
PWD_MD5=$(printf '%s' '<管理员明文密码>' | md5sum | cut -d' ' -f1)

curl -s -X POST "http://<设备地址>:8085/v1/login" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "username=admin" \
  -d "password=$PWD_MD5"

【警告】密码必须是 md5(明文) 的小写 hex。传明文会返回 账号或密码错误——而且这个报错和「密码真的错了」无法区分,极易误判。

实测成功响应(已截断 User.Menus):

json
{
  "code": 0,
  "count": 0,
  "data": {
    "User": {
      "Id": 37,
      "LoginName": "admin",
      "RealName": "admin",
      "Password": "",
      "Role": "1",
      "Platform": "arm",
      "Model": "IA-2230",
      "LastLogin": 1504115965,
      "LastIp": "192.168.50.70",
      "Menus": [ { "Id": 1, "AuthName": "设备状态", "EnglishName": "deviceState", "Auths": [ ] } ],
      "Status": 1,
      "UpdateTime": 1504115965
    },
    "IsFirst": false
  },
  "msg": "ok"
}
字段类型说明
data.Userobject登录用户对象(注意 Password 被清空
data.User.Menusarray该用户的菜单树,可用来反推产品的功能分区
data.IsFirstbool是否为首次启动InitFirstBoot() 的返回值);为 true 表示设备刚初始化
countint登录接口恒为 0不要拿它当成功标志

登录同时下发 3 个 Cookie(实测响应头):

http
Set-Cookie: beegosessionID=3df6f87c5276c43e6f8274d159830df5; Path=/; HttpOnly
Set-Cookie: lang=zh-CN; Expires=Mon, 17 Sep 2085 21:13:32 UTC; Max-Age=2147483647; Path=/
Set-Cookie: admin=37|3afddb6d2b26d2b7657c4bd601892d67|1504202365; Expires=Thu, 31 Aug 2017 17:59:25 UTC; Max-Age=86400; Path=/
Cookie作用必需
beegosessionIDbeego 服务端 Session 的凭据,对应过滤器步骤 7 的 session["user"]必需
admin用户名为名的业务 Cookie,值 = 用户ID|authkey|过期时间戳必需
lang界面语言,zh-CN / en-US可选

authkey 的构造(controllers/default.go):

go
authkey := libs.Md5([]byte(self.getClientIp() + "|" + user.Password + user.Salt))

→ 即 md5(客户端IP + "|" + 密码哈希 + 盐)注意:它绑定了登录时的客户端 IP。

测试请求携带结果
A只有 admin=37|fake|99999999999401
B只有 beegosessionID=<有效会话>401
C两者同时携带200 OK

二次开发必须把登录响应的全部 Cookie 存下来并在后续请求中原样带上,只带其中一个必然 401。

bash
# 用 cookie jar 是最省事的做法
curl -s -c /tmp/dass2.jar ...登录...
curl -s -b /tmp/dass2.jar "http://<设备地址>:8085/v1/das"

【警告】不要依赖 Cookie 过期时间自动管理。过滤器会在每次成功请求后把 admin Cookie 的过期时间滑动续期 24 小时,所以只要持续调用就不会掉线;但会话本身(beegosessionID)的有效期由 beego 控制。正确做法是:捕获 401 → 重新登录一次 → 重放原请求,而不是定时重登或无限重试。

2.3.4 本机免鉴权(重要,可省掉整个登录流程)

若配置项 Base.RequireLoginLogintrue,则来自 127.0.0.1 / localhost 的请求完全跳过鉴权(过滤器步骤 1)。

  • 配置文件键名:conf/dasserver.confrequire.local.login,注释写作「本地无鉴权访问」;代码里对应字段却叫 RequireLoginLoginglobal/conf.go,默认 false)。
  • 实测:该设备 GET /v1/system/conf 返回的 Base.RequireLoginLogintrue
json
{ "Base": { "RequireLoginLogin": true, "BoxCode": "default_0011", "MaxLengthServiceName": 11 } }

【注意】如果你的程序与 DASS2 跑在同一台设备上(例如设备侧的采集程序、脚本或系统服务),大概率可以走 http://127.0.0.1:8085/v1/... 免登录直接调用,实现最简单也最稳。
【警告】反过来看,这也是一个安全口子:命中该配置时,设备上任何本地进程都能无凭据调用全部 /v1 接口,且包含重建服务、清空测点、重启设备等破坏性操作。对外交付时建议评估是否关闭。
上表结论来自源码逻辑 + 实测配置值;由于本次没有该设备本机的执行权限,未在设备本机做环回调用的端到端实测,请在目标环境自行确认一次。

2.3.5 鉴权失败长什么样(务必按 HTML 处理)

FilterBefore 用的是 ctx.Redirect(401, "/"),所以:

bash
$ curl -i "http://<设备地址>:8085/v1/das"
HTTP/1.1 401 Unauthorized

<a href="/">Unauthorized</a>.
情况HTTP响应体
未登录 / Cookie 过期,调普通接口401<a href="/">Unauthorized</a>.(HTML,非 JSON)
未登录调 /v1/isLogin/302重定向到 /
未登录调 /v1/logout/500空(服务端 panic,见 7-已知限制与坑)

【警告】客户端不能对 401 直接 json.Unmarshal——那会得到一个 HTML 解析错误,把「未登录」误报成「响应格式错误」。先看 HTTP 状态码,401/302 直接走重登逻辑。

2.3.6 免鉴权路径清单(源码结论)

以下路径不需要登录(命中过滤器前 6 条步骤):

/                                  (前端页面)
/v1/login  /v1/login/              (含 "login")
/v1/logout  /v1/logout/            (含 "logout")
/v1/socket.io/...                  (含 "socket.io")
任何不含 "v1" 的路径

【警告】这里是子串匹配而非精确匹配:任何 URL 中只要含有 login 子串就会被放行。这属于实现缺陷而非有意设计,二次开发不要利用它绕过鉴权。另外 /v1/isLogin/ 不在放行名单里("isLogin" 含的是大写 L,匹配不到小写 login),所以它需要鉴权、失败时给 302。

2.4 dasId 从哪来

{id}(下文统称 dasId)是本手册出现频率最高的参数,也是最容易被误解的一个。

结论
真实语义采集服务在关系库 das_service 表的自增主键
不是什么不是测点 ID、不是驱动 ID、不是设备序列号、不是端口
怎么获取GET /v1/das,取 data[].id(配合 data[].name 一起记)
怎么传绝大多数接口是 URL 路径参数,形如 /v1/point/import/{dasId}
写死会失效自增主键随建删服务变化,不要硬编码,每次先查列表

实测 GET /v1/das

json
{
  "code": 0,
  "count": 2,
  "data": [
    { "id": 36, "name": "jen", "type": "GB_Demo_Jenkins", "driverType": "das",
      "port": 5480, "desc": "s", "host": "", "isLink": "true",
      "nodeName": "W3.JEN", "status": "running", "total": 0, "timeout": 0 },
    { "id": 35, "name": "mqtt", "type": "OP2_MQTT_JC", "driverType": "upload",
      "port": 9486, "desc": "m", "host": "", "isLink": "true",
      "nodeName": "W3.MQTT", "status": "stop", "total": 0, "timeout": 0 }
  ],
  "msg": "ok"
}
字段类型说明
idintdasId,后续接口要用的就是这个
namestring服务名,全局唯一,创建后用于 /v1/das/conf/third/v1/das/boot/{name} 等按名寻址的接口
typestring驱动名(如 GB_Demo_Jenkins),注意字段名叫 type 但存的是驱动名
driverTypestring驱动类型 / 用途:das=采集、upload=上传
portint该采集服务的本地监听端口(由系统自动分配,不传)
nodeNamestring实时库节点名,形如 W3.JEN(大写)
statusstringrunning / stop / error
isLinkstring字符串 "true"/"false",不是布尔:true = 测点自动写入实时库
descstring服务描述(创建时必填
total / timeoutint通道计数 / 超时;服务未运行时可能不返回

【注意】/v1/dascount服务总数status 来自内存态 DasServiceStatus 而非数据库,所以服务停了但进程还在时,这里可能仍显示 running——需要以 /v1/das/deviceLink/{id} 的真实链路状态为准。

2.5 调用前的自检清单

#检查项通过标准
1网络与端口8085 可连(curl -i http://<设备地址>:8085/ 能返回页面)
2登录成功code == 0 && msg == "ok",且拿到 beegosessionID + <用户名> 两个 Cookie
3鉴权生效带上两个 Cookie 调 GET /v1/das 返回 200 而不是 401
4目标采集服务存在GET /v1/dasdata[].id 里有你要的 dasId,且确认 status
5目标驱动存在GET /v1/driver 能看到目标驱动,且 driverType 与用途匹配(采集用 das,上传用 upload
6驱动配置字段已知GET /v1/driver/conf/{driverId} 能拿到字段清单(key / textType / textData),建服务前必须先拿这个
7实时库在线GET /v1/das/deviceLink/{dasId} 返回 data: true;否则测点导入/历史查询会异常
8授权余量充足GET /v1/mqtt/baseInfolicensePointuserdPoint 留有余量
9破坏性操作已确认已读 7-已知限制与坑的「破坏性接口清单」,确认不会误删数据 / 误重启设备