概述
App Inventor 2的 Web 组件可以发送HTTP请求,与任何RESTful API通信。这是App Inventor连接外部世界的核心组件,可以实现:
- 天气查询
- 新闻获取
- AI大模型对话
- 数据库CRUD操作
- 第三方服务集成
Web组件位于组件面板的 Connectivity 分类下。
Web组件核心方法
| 方法 | 说明 |
|---|---|
| Get(url) | 发送GET请求 |
| PostText(url, text, encoding) | 发送POST请求(文本) |
| PostFile(url, path) | 上传文件 |
| PutText(url, text, encoding) | 发送PUT请求 |
| Delete(url) | 发送DELETE请求 |
| ClearCookies() | 清除Cookie |
| BuildPostData(list) | 构建表单数据 |
核心事件
| 事件 | 参数 | 说明 |
|---|---|---|
| GotText(url, responseCode, responseType, responseContent) | 响应内容 | GET/POST请求返回 |
| GotFile(url, responseCode, responseType, fileName) | 文件名 | 文件下载返回 |
核心属性
| 属性 | 说明 |
|---|---|
| RequestHeaders | 请求头(列表) |
| AllowCookies | 是否允许Cookie |
| SaveResponse | 是否保存响应到文件 |
| ResponseFileName | 响应保存的文件名 |
GET请求
基本GET请求
when 按钮_查询.Click() {
Web客户端1.Get("https://api.example.com/data")
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
if 响应码 == 200 {
标签_结果.Text = 响应内容
} else {
标签_结果.Text = join("请求失败:", 响应码)
}
}
带参数的GET请求
when 按钮_搜索天气.Click() {
global 城市 = 输入框_城市.Text
global API密钥 = "your_api_key"
global url = join("https://api.openweathermap.org/data/2.5/weather?q=", 城市, "&appid=", API密钥, "&lang=zh_cn&units=metric")
Web客户端1.Get(url)
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
global 数据 = Web客户端1.JsonTextDecode(响应内容)
global 温度 = dictLookupPath(数据, list("main", "temp"), "")
global weatherList = dictLookup(数据, "weather", "")
global firstWeather = selectListItem(weatherList, 1)
global 描述 = dictLookup(firstWeather, "description", "")
标签_天气.Text = join("温度:", 温度, "°C,", 描述)
}
设置请求头
很多API需要在请求头中传递认证信息:
when 按钮_请求.Click() {
// 设置请求头
var 请求头 = [ "Authorization: Bearer your_token_here", "Content-Type: application/json", "Accept: application/json" ]
Web客户端1.RequestHeaders = 请求头
Web客户端1.Get("https://api.example.com/protected")
}
POST请求
发送JSON数据
when 按钮_提交.Click() {
global 请求数据 = join("{\"name\":\"", 文本输入框_姓名.Text, "\",\"age\":", 输入框_年龄.Text, "}")
global 请求头 = ["Content-Type: application/json"]
Web客户端1.RequestHeaders = 请求头
Web客户端1.PostText(
"https://api.example.com/users",
请求数据,
"UTF-8"
)
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
if 响应码 == 200 or 响应码 == 201 {
标签_结果.Text = "✅ 提交成功"
} else {
标签_结果.Text = join("❌ 失败:", 响应码)
}
}
发送表单数据
when 按钮_登录.Click() {
global 表单数据 = [
"username", 输入框_用户名.Text,
"password", 文本输入框_密码.Text
]
Web客户端1.RequestHeaders = ["Content-Type: application/x-www-form-urlencoded"]
Web客户端1.PostText("https://api.example.com/login",
Web客户端1.BuildPostData(表单数据), "UTF-8")
}
实战案例:调用AI大模型API
调用DeepSeek Chat API
global API地址 = "https://api.deepseek.com/chat/completions"
global API密钥 = "your_api_key"
when 按钮_发送.Click() {
global 用户消息 = 输入框_消息.Text
global 请求体 = join("{\"model\":\"deepseek-chat\",\"messages\":[{\"role\":\"user\",\"content\":\"", 用户消息, "\"}]}")
global 请求头 = [
"Content-Type: application/json",
join("Authorization: Bearer ", API密钥)
]
Web客户端1.RequestHeaders = 请求头
标签_状态.Text = "思考中..."
Web客户端1.PostText(API地址, 请求体, "UTF-8")
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
if 响应码 == 200 {
global 响应数据 = Web客户端1.JsonTextDecode(响应内容)
global 消息 = dictLookupPath(响应数据, list("choices", 1, "message", "content"), "")
标签_回复.Text = 消息
} else {
标签_回复.Text = join("请求失败:", 响应码)
}
标签_状态.Text = ""
}
实战案例:JSON API数据展示
获取和展示新闻列表
when Screen1.Initialize() {
Web客户端1.Get("https://newsapi.org/v2/top-headlines?country=cn&apiKey=your_key")
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
global 数据 = Web客户端1.JsonTextDecode(响应内容)
global 文章列表 = dictLookup(数据, "articles", "")
global 显示文本 = ""
global i = 1
while i <= 列表长度(文章列表) {
global 文章 = selectListItem(文章列表, i)
global 标题 = dictLookup(文章, "title", "")
global 来源 = dictLookupPath(文章, list("source", "name"), "")
显示文本 = join(显示文本, "📌 ", 标题, "\n 来源:", 来源, "\n\n")
i = i + 1
}
标签_新闻.Text = 显示文本
}
文件上传
上传图片到服务器
when 按钮_上传.Click() {
var 图片路径 = 图像选择器1.SavedRecording
if 图片路径 != "" {
Web客户端1.PostFile("https://api.example.com/upload", 图片路径)
}
}
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
if 响应码 == 200 {
标签_结果.Text = join("✅ 上传成功:", 响应内容)
}
}
错误处理
网络异常处理
when Web客户端1.GotText(网址, 响应码, 响应类型, 响应内容) {
if 响应码 == 200 {
// 成功处理
标签_结果.Text = 响应内容
} else if 响应码 == 401 {
标签_结果.Text = "❌ 认证失败,请检查API密钥"
} else if 响应码 == 403 {
标签_结果.Text = "❌ 无权限访问"
} else if 响应码 == 404 {
标签_结果.Text = "❌ 资源不存在"
} else if 响应码 == 429 {
标签_结果.Text = "❌ 请求过于频繁,请稍后重试"
} else if 响应码 == 500 {
标签_结果.Text = "❌ 服务器错误"
} else {
标签_结果.Text = join("❌ 请求失败:", 响应码)
}
}
HTTP状态码速查
| 状态码 | 含义 | 处理方式 |
|---|---|---|
| 200 | 成功 | 解析响应数据 |
| 201 | 创建成功 | POST请求成功 |
| 400 | 请求格式错误 | 检查请求体格式 |
| 401 | 未认证 | 检查API密钥 |
| 403 | 无权限 | 检查权限设置 |
| 404 | 资源不存在 | 检查URL |
| 429 | 请求频率超限 | 延迟重试 |
| 500 | 服务器错误 | 稍后重试 |
常见问题
Q1: Web组件请求返回空白?
- 检查URL是否正确(用浏览器测试)
- 检查是否需要设置请求头(如Authorization)
- 确认有INTERNET权限
- 检查HTTPS证书问题
Q2: JSON解析失败?
- 先用标签显示原始响应内容,检查格式
- 确认API返回的是JSON(不是XML或HTML错误页面)
- 使用
is a dictionary?积木块检查解析结果类型
Q3: 请求中文参数乱码?
对URL中的中文参数进行URL编码:
global 编码后城市 = Web客户端1.Url(城市名称)
global url = join("https://api.example.com/search?q=", 编码后城市)
Q4: 如何实现异步请求(不阻塞UI)?
Web组件本身就是异步的——调用Get/Post后会立即返回,UI继续响应。响应在GotText事件中处理。
总结
| 操作 | 方法 | 示例 |
|---|---|---|
| 查询数据 | Get | 天气查询、新闻获取 |
| 提交数据 | PostText | 登录、表单提交 |
| 上传文件 | PostFile | 图片上传 |
| 更新数据 | PutText | 修改用户信息 |
| 删除数据 | Delete | 删除记录 |
扫码添加客服咨询