PB调用WebAPI
2025/5/7大约 12 分钟
PB调用WebAPI
在现代开发中,PB 不再是孤岛。通过 Pbidea 扩展库调用 WebAPI,PB 应用可以轻松对接各种第三方服务、云平台,实现数据互通和功能扩展。
一、为什么PB需要调用WebAPI
1.1 传统PB的局限
| 局限 | 说明 |
|---|---|
| 技术封闭 | 传统C/S架构,难以与外部系统对接 |
| 功能受限 | 内置功能有限,无法实现复杂业务 |
| 数据孤岛 | 难以与其他系统共享数据 |
| 现代化困难 | 界面老旧,用户体验差 |
1.2 调用WebAPI的好处
┌─────────────┐ HTTP/HTTPS ┌─────────────┐
│ PB客户端 │ ═══════════════════► │ WebAPI服务 │
│ (传统C/S) │ ◄═══════════════════ │ (RESTful) │
└─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ 第三方服务 │
│ • 微信支付 │
│ • 短信平台 │
│ • 地图服务 │
│ • AI接口 │
└─────────────┘实际应用场景:
| 场景 | 说明 |
|---|---|
| 微信支付/支付宝 | 对接支付接口,实现线上收款 |
| 短信验证码 | 对接短信平台,实现用户验证 |
| 身份证识别 | 对接OCR接口,自动读取身份证信息 |
| 天气预报 | 对接气象API,显示实时天气 |
| 物流查询 | 对接快递接口,追踪物流信息 |
| 企业微信/钉钉 | 对接办公平台,发送通知消息 |
二、HTTP基础概念
2.1 HTTP请求方法
| 方法 | 说明 | PB应用场景 |
|---|---|---|
| GET | 获取资源 | 查询数据、获取列表 |
| POST | 提交数据 | 新增记录、提交表单 |
| PUT | 更新资源 | 修改数据 |
| DELETE | 删除资源 | 删除记录 |
2.2 HTTP状态码
| 状态码 | 含义 | 处理方式 |
|---|---|---|
| 200 | 请求成功 | 正常处理响应数据 |
| 400 | 请求参数错误 | 检查请求参数 |
| 401 | 未授权 | 检查Token是否有效 |
| 403 | 禁止访问 | 检查权限配置 |
| 404 | 接口不存在 | 检查接口地址 |
| 500 | 服务器内部错误 | 联系接口提供方 |
| 502/503 | 服务不可用 | 稍后重试 |
2.3 常见数据格式
JSON格式(最常用)
{
"code": 200,
"message": "成功",
"data": {
"userId": 1001,
"userName": "张三",
"age": 25,
"department": "技术部"
}
}三、Pbidea的HTTP客户端
Pbidea 提供了强大的 uo_httpclient 对象,让 PB 调用 WebAPI 变得简单高效。
3.1 uo_httpclient简介
uo_httpclient 是 Pbidea 封装的一个用户对象,基于 WinHTTP 实现,支持:
- GET/POST/PUT/DELETE 等常用请求方法
- 自动 UTF-8 编码转换
- JSON 数据的自动解析和生成
- 自定义请求头(Headers)
- 文件上传和下载
- 超时设置
- HTTPS 支持
3.2 引入Pbidea
在使用前,确保项目中已引入 Pbidea:
- 将
Pbidea.dll放入项目根目录 - 将
websuite.pbl添加到工作空间 - 在代码中声明
uo_httpclient对象
// 声明动态库(在websuite.pbl的uo_json中已声明)
// 直接使用即可
uo_httpclient luo_http四、基础请求方法
4.1 GET请求 - 获取数据
uo_httpclient luo_http
String ls_response
Integer li_status
luo_http = CREATE uo_httpclient
// 发送GET请求
ls_response = luo_http.get("https://jsonplaceholder.typicode.com/posts/1")
// 获取HTTP状态码
li_status = luo_http.get_status()
If li_status = 200 Then
MessageBox("响应", ls_response)
Else
MessageBox("错误", "HTTP状态码:" + String(li_status))
End If
DESTROY luo_http返回示例:
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit..."
}4.2 POST请求 - 提交数据
提交表单数据
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// 方式一:直接传入参数字符串
ls_response = luo_http.post("https://api.example.com/login", &
"username=admin&password=123456")
// 方式二:使用add_param添加参数
luo_http.add_param("username", "admin")
luo_http.add_param("password", "123456")
ls_response = luo_http.post("https://api.example.com/login")
DESTROY luo_http提交JSON数据
uo_httpclient luo_http
String ls_response, ls_json
luo_http = CREATE uo_httpclient
// 准备JSON数据
ls_json = '{"title":"foo","body":"bar","userId":1}'
// 发送POST请求(自动设置Content-Type为application/json)
ls_response = luo_http.post_json("https://jsonplaceholder.typicode.com/posts", ls_json)
MessageBox("响应", ls_response)
DESTROY luo_http4.3 PUT请求 - 更新数据
uo_httpclient luo_http
String ls_response, ls_json
luo_http = CREATE uo_httpclient
ls_json = '{"id":1,"title":"updated title","body":"updated body","userId":1}'
// PUT请求更新数据
ls_response = luo_http.put("https://jsonplaceholder.typicode.com/posts/1", ls_json)
MessageBox("响应", ls_response)
DESTROY luo_http4.4 DELETE请求 - 删除数据
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// DELETE请求删除数据
ls_response = luo_http.delete("https://jsonplaceholder.typicode.com/posts/1")
MessageBox("响应", ls_response)
DESTROY luo_http五、高级用法
5.1 自定义请求头
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// 添加自定义Header
luo_http.add_header("Authorization", "Bearer eyJhbGciOiJIUzI1NiIs...")
luo_http.add_header("X-Request-Id", "REQ202401010001")
luo_http.add_header("Accept-Language", "zh-CN")
// 发送请求
ls_response = luo_http.get("https://api.example.com/protected/data")
DESTROY luo_http5.2 设置超时时间
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// 设置超时时间(秒)
luo_http.set_timeout(30) // 30秒超时
ls_response = luo_http.get("https://api.example.com/slow-api")
DESTROY luo_http5.3 文件下载
uo_httpclient luo_http
String ls_response
Boolean lb_result
luo_http = CREATE uo_httpclient
// 下载文件到本地
lb_result = luo_http.download("https://example.com/file.pdf", &
"C:\\temp\\download\\file.pdf")
If lb_result Then
MessageBox("成功", "文件下载成功")
Else
MessageBox("失败", "文件下载失败")
End If
DESTROY luo_http5.4 文件上传
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// 添加文件参数
luo_http.add_file("file", "C:\\temp\\report.pdf")
luo_http.add_param("description", "月度报表")
// 上传文件
ls_response = luo_http.post("https://api.example.com/upload")
MessageBox("响应", ls_response)
DESTROY luo_http六、JSON数据处理
Pbidea 提供了 uo_json 对象,可以方便地解析和生成 JSON。
6.1 解析JSON响应
uo_httpclient luo_http
uo_json luo_json
String ls_response
Long ll_userId
String ls_userName, ls_title
luo_http = CREATE uo_httpclient
luo_json = CREATE uo_json
// 发送请求
ls_response = luo_http.get("https://jsonplaceholder.typicode.com/posts/1")
// 解析JSON
luo_json.parse(ls_response)
// 获取字段值(支持路径访问)
ll_userId = luo_json.get_item_number("userId")
ls_title = luo_json.get_item_string("title")
MessageBox("解析结果", "用户ID:" + String(ll_userId) + &
"~r~n标题:" + ls_title)
DESTROY luo_http
DESTROY luo_json6.2 解析嵌套JSON
uo_json luo_json
String ls_response
String ls_name, ls_city
Integer li_age
// 假设收到这样的嵌套JSON
ls_response = '{"code":200,"message":"成功","data":{"user":{"name":"张三","age":25,"address":{"city":"北京"}}}}'
luo_json = CREATE uo_json
luo_json.parse(ls_response)
// 使用点号路径访问嵌套字段
ls_name = luo_json.get_item_string("data.user.name") // 张三
li_age = luo_json.get_item_number("data.user.age") // 25
ls_city = luo_json.get_item_string("data.user.address.city") // 北京
MessageBox("用户信息", "姓名:" + ls_name + &
"~r~n年龄:" + String(li_age) + &
"~r~n城市:" + ls_city)
DESTROY luo_json6.3 解析JSON数组
uo_httpclient luo_http
uo_json luo_json, luo_item
String ls_response
Integer li_i, li_count
String ls_names
luo_http = CREATE uo_httpclient
luo_json = CREATE uo_json
// 获取列表数据
ls_response = luo_http.get("https://jsonplaceholder.typicode.com/users")
// 解析JSON数组
luo_json.parse(ls_response)
// 获取数组长度
li_count = luo_json.get_item_count()
ls_names = "用户列表(" + String(li_count) + "个):~r~n"
// 遍历数组
For li_i = 1 To li_count
luo_item = luo_json.get_item(li_i)
ls_names = ls_names + String(li_i) + ". " + &
luo_item.get_item_string("name") + &
" (" + luo_item.get_item_string("email") + ")~r~n"
Next
MessageBox("用户列表", ls_names)
DESTROY luo_http
DESTROY luo_json6.4 生成JSON数据
uo_json luo_json
String ls_jsonStr
luo_json = CREATE uo_json
// 添加字段
luo_json.add_item_string("userName", "张三")
luo_json.add_item_number("age", 25)
luo_json.add_item_string("department", "技术部")
luo_json.add_item_boolean("isActive", True)
// 添加嵌套对象
uo_json luo_address
luo_address = CREATE uo_json
luo_address.add_item_string("city", "北京")
luo_address.add_item_string("district", "海淀区")
luo_json.add_item_json("address", luo_address)
// 添加数组
uo_json luo_hobbies
luo_hobbies = CREATE uo_json
luo_hobbies.add_item_string("", "编程")
luo_hobbies.add_item_string("", "读书")
luo_hobbies.add_item_string("", "游泳")
luo_json.add_item_json("hobbies", luo_hobbies)
// 生成JSON字符串
ls_jsonStr = luo_json.to_string()
// 结果:
// {
// "userName": "张三",
// "age": 25,
// "department": "技术部",
// "isActive": true,
// "address": {"city":"北京","district":"海淀区"},
// "hobbies": ["编程","读书","游泳"]
// }
MessageBox("生成的JSON", ls_jsonStr)
DESTROY luo_json
DESTROY luo_address
DESTROY luo_hobbies七、完整实战示例
7.1 示例一:调用天气预报API
// 窗口函数:uf_getWeather(String as_city) RETURNS String
uo_httpclient luo_http
uo_json luo_json
String ls_url, ls_response, ls_result
String ls_city, ls_weather, ls_temperature
Integer li_status
luo_http = CREATE uo_httpclient
luo_json = CREATE uo_json
// 使用免费的天气API(示例,需替换为实际的API地址和Key)
ls_url = "https://api.weather.com/v1/current?city=" + as_city + "&appkey=your_key"
ls_response = luo_http.get(ls_url)
li_status = luo_http.get_status()
If li_status = 200 Then
luo_json.parse(ls_response)
// 解析天气信息
ls_city = luo_json.get_item_string("data.city")
ls_weather = luo_json.get_item_string("data.weather")
ls_temperature = luo_json.get_item_string("data.temperature")
ls_result = "城市:" + ls_city + "~r~n" + &
"天气:" + ls_weather + "~r~n" + &
"温度:" + ls_temperature + "℃"
Else
ls_result = "获取天气失败,状态码:" + String(li_status)
End If
DESTROY luo_http
DESTROY luo_json
Return ls_result7.2 示例二:调用短信发送API
// 窗口函数:uf_sendSMS(String as_phone, String as_content) RETURNS Integer
uo_httpclient luo_http
uo_json luo_json, luo_param
String ls_response
Integer li_status
Long ll_code
luo_http = CREATE uo_httpclient
luo_json = CREATE uo_json
luo_param = CREATE uo_json
// 短信平台API地址(示例,需替换为实际平台)
// 准备请求参数
luo_param.add_item_string("apiKey", "your_api_key")
luo_param.add_item_string("phone", as_phone)
luo_param.add_item_string("content", as_content)
// 发送POST请求
ls_response = luo_http.post_json("https://api.smsprovider.com/send", &
luo_param.to_string())
li_status = luo_http.get_status()
If li_status = 200 Then
luo_json.parse(ls_response)
ll_code = luo_json.get_item_number("code")
If ll_code = 0 Then
MessageBox("成功", "短信发送成功")
Return 1
Else
MessageBox("失败", luo_json.get_item_string("message"))
Return -1
End If
Else
MessageBox("请求失败", "HTTP状态码:" + String(li_status))
Return -1
End If
DESTROY luo_http
DESTROY luo_json
DESTROY luo_param7.3 示例三:对接企业微信发送消息
// 窗口函数:uf_sendWechatMessage(String as_userId, String as_content) RETURNS Integer
uo_httpclient luo_http
uo_json luo_json, luo_tokenParam, luo_msgParam
String ls_response, ls_token
Integer li_status
Long ll_errcode
luo_http = CREATE uo_httpclient
luo_json = CREATE uo_json
// ========== 第一步:获取AccessToken ==========
luo_tokenParam = CREATE uo_json
luo_tokenParam.add_item_string("corpid", "ww1234567890abcdef")
luo_tokenParam.add_item_string("corpsecret", "your_secret_here")
ls_response = luo_http.get("https://qyapi.weixin.qq.com/cgi-bin/gettoken?" + &
"corpid=" + luo_tokenParam.get_item_string("corpid") + &
"&corpsecret=" + luo_tokenParam.get_item_string("corpsecret"))
luo_json.parse(ls_response)
ls_token = luo_json.get_item_string("access_token")
If Trim(ls_token) = "" Then
MessageBox("错误", "获取Token失败:" + luo_json.get_item_string("errmsg"))
Return -1
End If
// ========== 第二步:发送消息 ==========
luo_msgParam = CREATE uo_json
luo_msgParam.add_item_string("touser", as_userId)
luo_msgParam.add_item_string("msgtype", "text")
luo_msgParam.add_item_number("agentid", 1000002)
uo_json luo_text
luo_text = CREATE uo_json
luo_text.add_item_string("content", as_content)
luo_msgParam.add_item_json("text", luo_text)
ls_response = luo_http.post_json("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + ls_token, &
luo_msgParam.to_string())
luo_json.parse(ls_response)
ll_errcode = luo_json.get_item_number("errcode")
If ll_errcode = 0 Then
MessageBox("成功", "消息发送成功")
li_status = 1
Else
MessageBox("失败", "错误码:" + String(ll_errcode) + &
"~r~n错误信息:" + luo_json.get_item_string("errmsg"))
li_status = -1
End If
DESTROY luo_http
DESTROY luo_json
DESTROY luo_tokenParam
DESTROY luo_msgParam
DESTROY luo_text
Return li_status7.4 示例四:对接RESTful接口完整CRUD
// 封装一个通用的RESTful操作类
// uo_rest_client(自定义类用户对象)
// ========== 实例变量 ==========
uo_httpclient iuo_http
String is_baseUrl
String is_token
// ========== 函数:初始化 ==========
// uf_init(String as_baseUrl, String as_token)
is_baseUrl = as_baseUrl
is_token = as_token
iuo_http = CREATE uo_httpclient
// 设置默认Header
iuo_http.add_header("Authorization", "Bearer " + is_token)
iuo_http.add_header("Content-Type", "application/json")
Return 1
// ========== 函数:查询列表 ==========
// uf_getList(String as_endpoint, ref String as_response) RETURNS Integer
String ls_url
ls_url = is_baseUrl + as_endpoint
as_response = iuo_http.get(ls_url)
If iuo_http.get_status() = 200 Then
Return 1
Else
Return -1
End If
// ========== 函数:查询详情 ==========
// uf_getDetail(String as_endpoint, Long al_id, ref String as_response) RETURNS Integer
String ls_url
ls_url = is_baseUrl + as_endpoint + "/" + String(al_id)
as_response = iuo_http.get(ls_url)
If iuo_http.get_status() = 200 Then
Return 1
Else
Return -1
End If
// ========== 函数:创建 ==========
// uf_create(String as_endpoint, String as_jsonData, ref String as_response) RETURNS Integer
String ls_url
ls_url = is_baseUrl + as_endpoint
as_response = iuo_http.post_json(ls_url, as_jsonData)
If iuo_http.get_status() = 201 Then
Return 1
Else
Return -1
End If
// ========== 函数:更新 ==========
// uf_update(String as_endpoint, Long al_id, String as_jsonData, ref String as_response) RETURNS Integer
String ls_url
ls_url = is_baseUrl + as_endpoint + "/" + String(al_id)
as_response = iuo_http.put(ls_url, as_jsonData)
If iuo_http.get_status() = 200 Then
Return 1
Else
Return -1
End If
// ========== 函数:删除 ==========
// uf_delete(String as_endpoint, Long al_id) RETURNS Integer
String ls_url
ls_url = is_baseUrl + as_endpoint + "/" + String(al_id)
iuo_http.delete(ls_url)
If iuo_http.get_status() = 204 Or iuo_http.get_status() = 200 Then
Return 1
Else
Return -1
End If
// ========== 析构函数 ==========
// Destructor
If IsValid(iuo_http) Then
DESTROY iuo_http
End If使用封装好的REST客户端:
uo_rest_client luo_client
uo_json luo_json, luo_data
String ls_response
Integer li_result
luo_client = CREATE uo_rest_client
luo_json = CREATE uo_json
// 初始化
luo_client.uf_init("https://api.example.com", "your_token_here")
// 查询员工列表
li_result = luo_client.uf_getList("/employees", ls_response)
If li_result = 1 Then
luo_json.parse(ls_response)
MessageBox("员工列表", "共 " + String(luo_json.get_item_count()) + " 人")
End If
// 创建新员工
luo_data = CREATE uo_json
luo_data.add_item_string("name", "张三")
luo_data.add_item_string("department", "技术部")
luo_data.add_item_number("salary", 8000)
li_result = luo_client.uf_create("/employees", luo_data.to_string(), ls_response)
If li_result = 1 Then
MessageBox("成功", "员工创建成功")
End If
// 更新员工
luo_data.add_item_number("salary", 9000)
li_result = luo_client.uf_update("/employees", 1001, luo_data.to_string(), ls_response)
// 删除员工
li_result = luo_client.uf_delete("/employees", 1001)
DESTROY luo_client
DESTROY luo_json
DESTROY luo_data八、常见问题与解决方案
8.1 中文乱码问题
// Pbidea的uo_httpclient默认使用UTF-8编码
// 如果遇到乱码,检查以下几点:
// 1. 确保服务器返回的是UTF-8编码
// 2. 检查请求头中是否设置了正确的编码
luo_http.add_header("Content-Type", "application/json; charset=utf-8")
// 3. 如果服务器返回GBK编码,需要转换
String ls_gbkResponse, ls_utf8Response
ls_gbkResponse = luo_http.get("https://api.example.com/gbk-api")
// 使用uo_string进行编码转换
uo_string luo_str
luo_str = CREATE uo_string
ls_utf8Response = luo_str.from_gbk(ls_gbkResponse)
DESTROY luo_str8.2 HTTPS证书问题
// 测试环境可以忽略证书验证
// 生产环境需要正确配置证书
// 使用uo_httpclient时,默认支持HTTPS
// 如果遇到证书错误,检查:
// 1. 系统时间是否正确(证书有有效期)
// 2. 证书是否过期
// 3. 证书链是否完整8.3 超时处理
uo_httpclient luo_http
String ls_response
luo_http = CREATE uo_httpclient
// 设置超时时间(秒)
luo_http.set_timeout(30)
// 对于大文件下载,可以设置更长的超时
luo_http.set_timeout(120)
ls_response = luo_http.get("https://api.example.com/large-data")
If luo_http.get_status() = 0 Then
MessageBox("超时", "请求超时,请检查网络")
End If
DESTROY luo_http8.4 异步调用(不阻塞界面)
// uo_httpclient默认是同步调用
// 如果需要异步,可以使用SharedObject
// 方式一:使用SharedObject
SharedObject lso_worker
lso_worker = CREATE SharedObject
lso_worker.Register("uo_http_async", "uo_http_async")
// 在SharedObject中执行HTTP请求
// 完成后通过PostEvent通知主窗口
// 方式二:使用定时器模拟异步
// 在窗口中放置定时器,定时检查请求结果九、API调试工具推荐
在 PB 中调试 API 之前,建议先用以下工具测试:
| 工具 | 用途 | 特点 |
|---|---|---|
| Postman | API测试 | 图形界面,功能强大 |
| Apifox | API测试+文档 | 国产工具,中文支持好 |
| curl | 命令行测试 | 轻量,适合脚本 |
| 浏览器F12 | 查看网络请求 | 调试Web应用 |
调试步骤:
- 在 Postman 中测试 API,确认接口可用
- 记录正确的请求参数、Header、Body
- 将配置移植到 PB 代码中
- 在 PB 中调试,对比 Postman 和 PB 的请求差异
十、安全注意事项
10.1 API密钥管理
// 不要硬编码密钥在代码中!
// 不好的做法
String ls_apiKey
ls_apiKey = "sk-1234567890abcdef" // 危险!
// 好的做法:从配置文件读取
ls_apiKey = ProfileString("config.ini", "API", "apiKey", "")
// 更好的做法:从数据库或加密配置读取
// 使用Pbidea的加密功能
uo_crypto luo_crypto
luo_crypto = CREATE uo_crypto
ls_apiKey = luo_crypto.decrypt(encrypted_key)
DESTROY luo_crypto10.2 数据传输安全
| 措施 | 说明 |
|---|---|
| 使用HTTPS | always使用HTTPS,不用HTTP |
| 验证证书 | 生产环境验证服务器证书 |
| 参数校验 | 对输入参数进行校验和过滤 |
| 签名验证 | 重要接口使用签名机制 |
十一、小结
通过本篇文章,我们学习了使用 Pbidea 调用 WebAPI 的完整流程:
| 知识点 | 掌握要点 |
|---|---|
| HTTP基础 | 请求方法、状态码、数据格式 |
| uo_httpclient | GET/POST/PUT/DELETE、Header、超时 |
| uo_json | 解析、生成、嵌套、数组 |
| 实战示例 | 天气、短信、企业微信、RESTful CRUD |
| 问题排查 | 乱码、证书、超时、异步 |
Pbidea HTTP调用速查表
| 操作 | 代码 |
|---|---|
| GET请求 | luo_http.get(url) |
| POST表单 | luo_http.post(url, params) |
| POST JSON | luo_http.post_json(url, json) |
| PUT请求 | luo_http.put(url, data) |
| DELETE请求 | luo_http.delete(url) |
| 添加Header | luo_http.add_header(key, value) |
| 添加参数 | luo_http.add_param(key, value) |
| 设置超时 | luo_http.set_timeout(seconds) |
| 获取状态码 | luo_http.get_status() |
| 下载文件 | luo_http.download(url, path) |
| 上传文件 | luo_http.add_file(key, path) |
学习建议
- 先学Postman:在PB编码前,先用Postman验证接口
- 从简单GET开始:先掌握获取数据,再学提交数据
- 善用Pbidea:uo_httpclient + uo_json 组合使用
- 注意安全性:密钥管理、HTTPS、参数校验
掌握 WebAPI 调用后,你的 PB 系统就能:
- 对接微信支付、短信平台等第三方服务
- 与Java、.NET等现代系统互联互通
- 为后续的 PB→Spring Boot 转型 打下基础
PB 调用 WebAPI 是连接传统与现代的桥梁,掌握它,你的PB系统就能焕发新生。
以上就是本期内容的全部 ★,°:.☆( ̄▽ ̄)/$:.°★ 。 希望对您有所帮助
我们下期再见 ヾ(•ω•`)o (●'◡'●)
