Remote - HTTP 客户端
介绍
在我们的应用中,经常需要向外部服务发起 HTTP 请求。这可以通过 _remote 资源来完成。
无论是在内部调用其他服务或本机上的内部服务器,还是调用互联网上的某个服务,使用 Remote 资源都是必不可少的。
请参阅 Remote 资源文档。
我们甚至可以发送和接收 JSON 数据,以及上传或下载文件。
初始化
要发起 HTTP 调用,首先必须初始化它,为此我们将使用 _remote 的 init 方法,然后将此操作的结果保存在名为 remote 的变量中,这样就能引用该请求:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
// 支持发送 HTTP 参数
const simpleRemote = _remote.init();
// 支持发送 JSON:
const jsonRemote = _remote.init().asJSON();
// 支持发送 HTTP 参数
const simpleRemote = _remote.init();
// 支持发送 JSON:
const jsonRemote = _remote.init().asJSON();
# 支持发送 HTTP 参数
simpleRemote = _remote.init()
# 支持发送 JSON:
jsonRemote = _remote.init().asJSON()
# 支持发送 HTTP 参数
simpleRemote = _remote.init()
# 支持发送 JSON:
jsonRemote = _remote.init().asJSON()
// 支持发送 HTTP 参数
val simpleRemote = _remote.init()
// 支持发送 JSON:
val jsonRemote = _remote.init().asJSON()
// 支持发送 HTTP 参数
final simpleRemote = _remote.init()
// 支持发送 JSON:
final jsonRemote = _remote.init().asJSON()
配置
我们还可以在应用中定义全局配置,请看这个完整示例:
...
"remote": {
"example-basic-auth": {
"url": "https://exemplo.com/api/rest/exemplo",
"form": true,
"authorization": {
"username": "admin",
"password": "pass"
},
"data": {
"param1": "值 1...",
"param2": "值 2..."
}
},
"example-auth-token": {
"urlPrefix": "https://exemplo.com/api",
"json": true,
"authorization": "Bearer AUTH-TOKEN-HERE",
"connectTimeout": 5000,
"readTimeout": 60000,
}
},
...
我们可以配置 URL 前缀(urlPrefix)或完整 URL(url)。
我们可以定义请求默认以 Form 或 JSON 形式发送数据。
支持使用用户名和密码或令牌发送基本授权。
还可以定义默认发送的数据(data),以及连接超时(connectTimeout)和读取超时(readTimeout)。
如何使用
要使用应用中定义的配置来使用 Remote 资源,只需使用配置键来初始化该资源,例如:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const response = _remote.init("example-basic-auth").post();
const response = _remote.init("example-basic-auth").post();
response = _remote.init("example-basic-auth").post()
response = _remote.init("example-basic-auth").post()
val response = _remote.init("example-basic-auth").post()
final response = _remote.init("example-basic-auth").post()
如果定义了前缀 URL:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init("example-auth-token");
const response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
);
const remote = _remote.init("example-auth-token");
const response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
);
remote = _remote.init("example-auth-token")
response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
)
remote = _remote.init("example-auth-token")
response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
)
val remote = _remote.init("example-auth-token")
val response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
)
final remote = _remote.init("example-auth-token")
final response = remote.post(
"/services/load-data",
_val.map()
.set("param1", "值 1")
.set("param2", "值 2")
)
HTTP 请求
看看如何使用 Remote 资源发起 HTTP 请求。
发送 JSON
要发送 JSON 格式的数据,我们需要将内容类型标记为 JSON:
最简单的方法是使用 asJSON() 方法,例如:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
remote.setURL("http://exemplo.com/save");
const response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
);
const remote = _remote.init().asJSON();
remote.setURL("http://exemplo.com/save");
const response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
);
remote = _remote.init().asJSON()
remote.setURL("http://exemplo.com/save")
response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
remote = _remote.init().asJSON()
remote.setURL("http://exemplo.com/save")
response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
val remote = _remote.init().asJSON()
remote.setURL("http://exemplo.com/save")
val response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
final remote = _remote.init().asJSON()
remote.setURL("http://exemplo.com/save")
final response = remote.post(
_val.map()
.set("data", "example")
.set("json", true)
)
这样,数据会以 JSON 形式发送给 HTTP 的 POST 和 PUT 方法,但对于 GET 和 DELETE,数据始终通过查询字符串发送,因为 HTTP 协议不支持这些方法携带包含数据的请求体。
HTTP 方法
我们不需要在调用的请求头中显式指明 HTTP 方法,因为我们的调用引用为每种 HTTP 方法都提供了对应的函数:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init();
remote.setURL("http://exemplo.com");
remote.get(); // 用于 GET 调用
remote.post(); // 用于 POST 调用
remote.put(); // 用于 PUT 调用
remote.delete(); // 用于 DELETE 调用
const remote = _remote.init();
remote.setURL("http://exemplo.com");
remote.get(); // 用于 GET 调用
remote.post(); // 用于 POST 调用
remote.put(); // 用于 PUT 调用
remote.delete(); // 用于 DELETE 调用
remote = _remote.init()
remote.setURL("http://exemplo.com")
remote.get() # 用于 GET 调用
remote.post() # 用于 POST 调用
remote.put() # 用于 PUT 调用
remote.delete() # 用于 DELETE 调用
remote = _remote.init()
remote.setURL("http://exemplo.com")
remote.get() # 用于 GET 调用
remote.post() # 用于 POST 调用
remote.put() # 用于 PUT 调用
remote.delete() # 用于 DELETE 调用
val remote = _remote.init()
remote.setURL("http://exemplo.com")
remote.get() // 用于 GET 调用
remote.post() // 用于 POST 调用
remote.put() // 用于 PUT 调用
remote.delete() // 用于 DELETE 调用
final remote = _remote.init()
remote.setURL("http://exemplo.com")
remote.get() // 用于 GET 调用
remote.post() // 用于 POST 调用
remote.put() // 用于 PUT 调用
remote.delete() // 用于 DELETE 调用
HTTP 头
要配置 HTTP 头参数,我们可以使用 getHeader() 方法设置键,例如:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init();
remote.getHeader().set("KEY", "VALUE");
const remote = _remote.init();
remote.getHeader().set("KEY", "VALUE");
remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
val remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
final remote = _remote.init()
remote.getHeader().set("KEY", "VALUE")
请求 URL
要指定请求的 URL,只需将其作为相应 HTTP 方法函数的第一个参数传入:
const remote = _remote.init().asJSON()
remote.get("http://exemplo.com") // 用于 GET 调用
remote.post("http://exemplo.com") // 用于 POST 调用
remote.put("http://exemplo.com") // 用于 PUT 调用
remote.delete("http://exemplo.com") // 用于 DELETE 调用
向请求添加数据
如果需要向请求体添加数据,只需将其作为相应方法函数的第二个参数传入:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
remote.get("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 DELETE 调用
const remote = _remote.init().asJSON();
remote.get("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)); // 用于 DELETE 调用
remote = _remote.init().asJSON()
remote.get("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 DELETE 调用
remote = _remote.init().asJSON()
remote.get("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)) # 用于 DELETE 调用
val remote = _remote.init().asJSON()
remote.get("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 DELETE 调用
final remote = _remote.init().asJSON()
remote.get("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 GET 调用
remote.post("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 POST 调用
remote.put("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 PUT 调用
remote.delete("http://exemplo.com", _val.map().set("user_id", 1)) // 用于 DELETE 调用
响应
收到请求响应后,我们可以使用 json 或 content 方法读取其数据:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const remote = _remote.init().asJSON();
const response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
);
if (response.ok()) {
_log.info("JSON 响应", response.json());
} else {
_log.info("失败,状态码:" + response.statusCode(), response.content());
}
const remote = _remote.init().asJSON();
const response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
);
if (response.ok()) {
_log.info("JSON 响应", response.json());
} else {
_log.info("失败,状态码:" + response.statusCode(), response.content());
}
remote = _remote.init().asJSON()
response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
)
if response.ok():
_log.info("JSON 响应", response.json())
else:
_log.info(
f"失败,状态码:{response.statusCode()}",
response.content()
)
remote = _remote.init().asJSON()
response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
)
if response.ok()
_log.info("JSON 响应", response.json())
else
_log.info(
"失败,状态码:#{response.statusCode()}",
response.content()
)
end
val remote = _remote.init().asJSON()
val response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
)
if (response.ok()) {
_log.info("JSON 响应", response.json())
} else {
_log.info(
"失败,状态码:${response.statusCode()}",
response.content()
)
}
final remote = _remote.init().asJSON()
final response = remote.post(
"http://exemplo.com",
_val.map().set("user_id", 1)
)
if (response.ok()) {
_log.info("JSON 响应", response.json())
} else {
_log.info("失败,状态码:" + response.statusCode(), response.content())
}
在响应中,使用 json() 函数可以获得作为 Values 提供的数组或对象,使用 content() 函数可以获得原始文本内容或 HTML,接收到的状态码数字通过 statusCode() 获取,而 ok() 返回一个布尔值,当状态码在 200 到 299 范围内时为 true。
下载
要下载文件,即获取文件,我们使用 remote 的 asBinary() 来表示应进行二进制传输。
请看这个完整的服务示例:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const download = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!download.ok()) {
_log.error(`文件下载状态:${download.statusCode()}\n${download.toString()}`);
_header.status(500);
_out.json({result: false, error: '文件下载失败'});
_exec.stop();
}
const file = download.file();
_log.info(`下载的文件名:${file.name()}`);
file.save(_app.file(`storage/filesystem/server/${file.name()}`));
file.save(_storage.filesystem("server", "other.png").file());
file.save(_os.file("/tmp/download.png"));
_out.json({result: true});
const download = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png');
if (!download.ok()) {
_log.error(`文件下载状态:${download.statusCode()}\n${download.toString()}`);
_header.status(500);
_out.json({result: false, error: '文件下载失败'});
_exec.stop();
}
const file = download.file();
_log.info(`下载的文件名:${file.name()}`);
file.save(_app.file(`storage/filesystem/server/${file.name()}`));
file.save(_storage.filesystem("server", "other.png").file());
file.save(_os.file("/tmp/download.png"));
_out.json({result: true});
download = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if not download.ok():
_log.error(f"文件下载状态:{download.statusCode()}\n{download.toString()}")
_header.status(500)
_out.json(_val.map().set("result", False).set("error", "文件下载失败"))
_exec.stop()
file = download.file()
_log.info(f"下载的文件名:{file.name()}")
file.save(_app.file(f"storage/filesystem/server/{file.name()}"))
file.save(_storage.filesystem("server", "other.png").file())
file.save(_os.file("/tmp/download.png"))
_out.json(_val.map().set("result", True))
download = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
unless download.ok()
_log.error("文件下载状态:#{download.statusCode()}\n#{download.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "文件下载失败"))
_exec.stop()
end
file = download.file()
_log.info("下载的文件名:#{file.name()}")
file.save(_app.file("storage/filesystem/server/#{file.name()}"))
file.save(_storage.filesystem("server", "other.png").file())
file.save(_os.file("/tmp/download.png"))
_out.json(_val.map().set("result", true))
val download = _remote.asBinary().get(
"https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png"
)
if (!download.ok()) {
_log.error("文件下载状态:${download.statusCode()}\n${download.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "文件下载失败"))
_exec.stop()
}
file = download.file()
_log.info("下载的文件名:${file.name()}")
file.save(_app.file("storage/filesystem/server/${file.name()}"))
file.save(_storage.filesystem("server", "other.png").file())
file.save(_os.file("/tmp/download.png"))
_out.json(_val.map().set("result", true))
final download = _remote.asBinary()
.get('https://raw.githubusercontent.com/netuno-org/platform/main/docs/images/overview.png')
if (!download.ok()) {
_log.error(`文件下载状态:${download.statusCode()}\n${download.toString()}`)
_header.status(500)
_out.json({result: false, error: '文件下载失败'})
_exec.stop()
}
final file = download.file()
_log.info(`下载的文件名:${file.name()}`)
file.save(_app.file(`storage/filesystem/server/${file.name()}`))
file.save(_storage.filesystem("server", "other.png").file())
file.save(_os.file("/tmp/download.png"))
_out.json({result: true})
这里故意使用了 3 次
file.save,要了解更多关于_app.file、_storage和_os.file的内容, 请参阅 文件和文件夹操作 教程。
上传
要以 multipart 格式将文件作为表单发送,我们使用 asMultipartFormData()。
请看一个完整服务示例:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const file = _app.file("public/images/logo.png");
const upload = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("file", file)
);
if (upload.ok()) {
_log.info(`上传成功:${file.name()}`);
_out.json({result: true});
} else {
_log.error(`文件上传状态:${upload.statusCode()}\n${upload.toString()}`);
_header.status(500);
_out.json({result: false, error: '文件上传失败'});
}
const file = _app.file("public/images/logo.png");
const upload = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("file", file)
);
if (upload.ok()) {
_log.info(`上传成功:${file.name()}`);
_out.json({result: true});
} else {
_log.error(`文件上传状态:${upload.statusCode()}\n${upload.toString()}`);
_header.status(500);
_out.json({result: false, error: '文件上传失败'});
}
file = _app.file("public/images/logo.png")
upload = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("file", file)
)
if upload.ok():
_log.info(f"上传成功:{file.name()}")
_out.json(_val.map().set("result", True))
else:
_log.error(f"文件上传状态:{upload.statusCode()}\n{upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", False).set("error", "文件上传失败"))
file = _app.file("public/images/logo.png")
upload = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("file", file)
)
if upload.ok()
_log.info("上传成功:#{file.name()}")
_out.json(_val.map().set("result", true))
else
_log.error("文件上传状态:#{upload.statusCode()}\n#{upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "文件上传失败"))
end
val file = _app.file("public/images/logo.png")
val upload = _remote.asMultipartFormData().post(
"http://localhost:9000/services/file-receiver",
_val.map().set("file", file)
)
if (upload.ok()) {
_log.info("上传成功:${file.name()}")
_out.json(_val.map().set("result", true))
} else {
_log.error("文件上传状态:${upload.statusCode()}\n${upload.toString()}")
_header.status(500)
_out.json(_val.map().set("result", false).set("error", "文件上传失败"))
}
final file = _app.file("public/images/logo.png")
final upload = _remote.asMultipartFormData()
.post(
'http://localhost:9000/services/file-receiver',
_val.map()
.set("file", file)
)
if (upload.ok()) {
_log.info(`上传成功:${file.name()}`)
_out.json({result: true})
} else {
_log.error(`文件上传状态:${upload.statusCode()}\n${upload.toString()}`)
_header.status(500)
_out.json({result: false, error: '文件上传失败'})
}
请注意,在上面的示例中,文件被发送到另一个服务,看看这个接收文件的另一个服务在 Netuno 中如何实现:
- JavaScript
- TypeScript
- Python
- Ruby
- Kotlin
- Groovy
const file = _req.getFile("file");
_log.info(`接收到的文件名:${file.name()}`);
file.save(_storage.filesystem("server", "received.png").file());
_out.json({result: true});
const file = _req.getFile("file");
_log.info(`接收到的文件名:${file.name()}`);
file.save(_storage.filesystem("server", "received.png").file());
_out.json({result: true});
file = _req.getFile("file")
_log.info(f"接收到的文件名:{file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", True))
file = _req.getFile("file")
_log.info("接收到的文件名:#{file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", true))
val file = _req.getFile("file")
_log.info("接收到的文件名:${file.name()}")
file.save(_storage.filesystem("server", "received.png").file())
_out.json(_val.map().set("result", true))
final file = _req.getFile("file")
_log.info(`接收到的文件名:${file.name()}`)
file.save(_storage.filesystem("server", "received.png").file())
_out.json({result: true})
结论
借助 Remote 资源,我们可以集成外部 API,可以导出、导入或同步数据。
我们甚至可以集成用其他技术实现的服务,从而执行其他语言提供的特定操作。