本文最后更新于 55 天前,其中的信息可能已经有所发展或是发生改变。
由于 QML 的 JavaScript 兼容性,我们可以直接使用 JavaScript 的 XMLHttpRequest
对象进行 HTTP 请求。QML 的 XMLHttpRequest
实现与标准浏览器的实现非常相似,但有一些限制和特殊行为需要注意。
而QML实现TCP等其他通信一般就需要借助Qt与C++的关系了,本文对此略过
1. XMLHttpRequest对象详解
1.1 属性
readyState
:当前对象的状态码,取值如下:0
(UNSENT):未初始化,尚未调用open()
。1
(OPENED):已调用open()
,但未调用send()
。2
(HEADERS_RECEIVED):已接收响应头。3
(LOADING):正在接收响应体。4
(DONE):请求完成,响应数据就绪。
status
:int,当前HTTP请求的状态码(如200
成功,404
未找到,见3.3)。statusText
:string,HTTP响应的状态文本(如 "OK"、"Not Found" 等,见3.3)。只有在readyState
为DONE
时才有效。responseType
:string,响应内容的数据类型。responseText
:string,服务器的响应数据,以纯文本形式表示。仅在responseType
为text
或未设置时有效response
:var,根据responseType
解析后的响应数据(如 JSON 对象、二进制数据)。默认为""
(空字符串),在 QML 中可以通过设置responseType
来获取不同类型的数据。responseURL
:string,Qt6.6引入,实际响应的URL,如果请求被重定向,则返回最终的 URL。responseXML
:var,服务器的响应数据,以XML文档形式表示,仅当响应内容为XML格式时有效。timeout
:int,超时时间(以毫秒为单位)。如果设置,当请求超过指定时间仍未完成时会触发ontimeout
事件。withCredentials
:bool,表示是否在跨域请求中发送凭据(如 Cookies)。仅在支持 CORS 的服务器上有效。
1.2 方法
void open(method, url, async)
:初始化请求。method
:HTTP 方法(如 GET、POST、PUT)。url
:请求地址(支持相对路径和绝对路径)。async
:是否异步(可省略,默认为true
,QML中通常不推荐设为false
)。
void send(data)
:发送请求。data
:获取的请求体内容(GET 请求可省略或传null
,POST 可传FormData
或 JSON 字符串)。
void setRequestHeader(header, value)
:设置请求头(需在open()
之后、send()
之前调用)。void abort()
:终止当前请求。string getAllResponseHeaders()
:string getResponseHeader(headerName)
:
1.3 事件
onreadystatechange
:function,函数类型,每当readyState
在几个状态中发生变化时就会触发,执行函数,用于监听请求状态。一般为其指定函数,用于指定处理各种状态变化后的事件。onerror
:function,函数类型,网络错误时触发(如域名解析失败),执行函数。ontimeout
:function,函数类型,请求超时时触发(需先设置timeout
属性),执行函数。onload
:function,函数类型,请求成功完成时触发(readyState === DONE
且status === 200
),执行函数。onprogress
:function,函数类型,上传或下载进度更新时触发(适用于大文件传输),执行函数。
2. XMLHttpRequest对象的使用
总示例如下:
function postCMD(cmd){
httpRequest("http://127.0.0.1:7111/track/" + cmd, "POST", null, function(resultS, status){
if(status === 200){
}else{
/// \todo warning message
}
})
}
function httpRequest(serverUrl, method, data, callback){
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED){
}else if(xhr.readyState === XMLHttpRequest.DONE){
if(callback){
callback(xhr.responseText.toString() ,xhr.status)
}
}
}
xhr.open(method, serverUrl);
xhr.timeout = 3000;
xhr.setRequestHeader("Connection", "close");
if(!data){
xhr.send();
}else{
xhr.send(data);
}
}
2.1 使用XMLHttpRequest的流程
- 创建
XMLHttpRequest
对象。 - 调用
open()
配置请求。 - 设置请求头。(可选)
- 设置
responseType
。(可选) - 监听
onreadystatechange
,处理响应。(在发送请求之前配置即可即可) - 调用
void send(data)
方法发送请求并传送数据。
2.2 配置请求的基本信息和请求头
- 使用
new
创建对象 - 使用
void open(method, url, async)
初始化请求,指定HTTP方法和请求地址。 - 使用
void setRequestHeader(header, value)
指定请求头的key和value,可累积配置。 - 还可以指定一些其他参数,如通过设置
timeout
属性来设置时间限制等等。
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:12345/");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.setRequestHeader("Authorization", "Bearer your_token_here");
xhr.timeout = 3000;
2.3 监听事件状态进行对应处理
在需要的时候为事件赋值函数,类似于回调函数,让在对应状态发生变化的时候触发此函数来获得想要的结果。
一般都会对readyState
的onreadystatechange
事件进行监听,以对请求的状态有着整体的把握。并且能在请求结束的时候,通过responseText
等属性获取GET的数据等。
xhr.onreadystatechange = function() {
console.log("当前 readyState:", xhr.readyState);
if (xhr.readyState === XMLHttpRequest.DONE) { // 请求完成
if (xhr.status === 200) { // 成功响应
console.log("请求成功!");
console.log("响应数据:", xhr.responseText);
} else {
console.error("请求失败,状态码:", xhr.status);
}
}
}
xhr.onerror = function() {
console.error("网络错误: 无法连接到服务器");
}
// 对于ontimeout、onload、onprogress都是同理
2.4 发送请求
在配置完成后使用void send(data)
方法发送请求并传送数据。
对于参数data
,如果是 GET 请求可省略或传 null,而如果是 POST 可传 FormData 或 JSON 字符串。
// GET时候可用
xhr.send();
// 通用
var data = '';
data = "...."; // 数据
xhr.send(data);
3. 进阶用法
3.1 发送 FormData(表单数据)
适用于文件上传或 multipart/form-data
格式:
function uploadFile() {
var xhr = new XMLHttpRequest();
var formData = new FormData();
formData.append("file", fileInput.files[0]); // 假设 fileInput 是文件选择框
formData.append("comment", "QML 文件上传测试");
xhr.open("POST", "https://api.example.com/upload");
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
console.log("上传结果:", xhr.status);
}
};
xhr.send(formData);
}
3.2 处理二进制数据(如图片)
通过设置 responseType
为 "arraybuffer"
,可直接获取二进制数据:
function loadImage() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://example.com/image.png");
xhr.responseType = "arraybuffer";
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
var byteArray = new Uint8Array(xhr.response);
var imageData = "data:image/png;base64," + btoa(String.fromCharCode.apply(null, byteArray));
imageElement.source = imageData; // 显示图片
}
};
xhr.send();
}
3.3 常见请求的状态码
状态码 | 含义 | 典型场景 |
---|---|---|
200 | 请求成功 | 常规 GET/POST 请求 |
201 | 资源已创建 | 创建新用户、提交表单 |
204 | 请求成功,但无内容 | DELETE 请求 |
301 | 资源永久移动 | URL 重写 |
302 | 资源临时移动 | 登录后跳转 |
304 | 资源未修改,使用缓存 | 条件请求 |
400 | 请求无效 | 表单数据错误 |
401 | 未授权 | 需要登录 |
403 | 禁止访问 | 权限不足 |
404 | 资源不存在 | 页面或 API 路径错误 |
405 | 方法不被允许 | 不支持的 HTTP 方法 |
429 | 请求过多 | 触发速率限制 |
500 | 服务器内部错误 | 代码崩溃或数据库问题 |
502 | 网关错误 | 上游服务不可用 |
503 | 服务不可用 | 服务器维护或过载 |
504 | 网关超时 | 上游服务响应超时 |
3.4 跨域请求与 withCredentials
跨域请求是常见的需求,特别是在需要携带 Cookies 或认证信息时。通过设置 withCredentials
属性为 true
,可以在跨域请求中发送凭据。
要注意的是,服务器必须支持 CORS(跨域资源共享),并允许 credentials
,如果服务器未正确配置 CORS,可能会导致请求失败。
function makeCORSRequest() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data", true);
xhr.withCredentials = true; // 允许跨域请求携带凭据
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
console.log("跨域请求成功:", xhr.responseText);
} else {
console.error("跨域请求失败,状态码:", xhr.status);
}
}
};
xhr.send();
}
3.5 利用onprogress事件监控上传/下载进度
对于大文件下载,可以通过 onprogress
事件实时更新下载进度,并显示给用户。
function downloadFile(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onprogress = function(event) {
if (event.lengthComputable) {
var percentComplete = (event.loaded / event.total) * 100;
console.log("下载进度:", percentComplete.toFixed(2) + "%");
} else {
console.log("无法计算总大小,正在下载...");
}
};
xhr.onload = function() {
if (xhr.status === 200) {
console.log("文件下载完成!");
} else {
console.error("下载失败,状态码:", xhr.status);
}
};
xhr.onerror = function() {
console.error("网络错误: 无法连接到服务器");
};
xhr.send();
}
类似地,对于大文件上传,也可以通过 onprogress
事件监控上传进度。
function uploadFile(fileInput) {
var xhr = new XMLHttpRequest();
var formData = new FormData();
formData.append("file", fileInput.files[0]);
xhr.open("POST", "https://api.example.com/upload", true);
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percentComplete = (event.loaded / event.total) * 100;
console.log("上传进度:", percentComplete.toFixed(2) + "%");
} else {
console.log("无法计算总大小,正在上传...");
}
};
xhr.onload = function() {
if (xhr.status === 200) {
console.log("文件上传成功!");
} else {
console.error("上传失败,状态码:", xhr.status);
}
};
xhr.onerror = function() {
console.error("网络错误: 无法连接到服务器");
};
xhr.send(formData);
}