# HTTP请求

在Qt中,通常,HTTP请求使用来自c++端的QNetworkRequestQNetworkReply来完成,然后使用Qt/C++将响应推送集成到QML空间中。 因此,在这里尝试稍微突破一下,使用当前Qt Quick提供的工具与网络端点进行通信。 为此,使用辅助对象来发出HTTP请求、响应循环,它以javascript的XMLHttpRequest对象的形式呈现。

XMLHttpRequest对象允许用户注册一个响应处理函数和一个URL。 可以使用HTTP动词(get、post、put、delete……)之一发出请求;当响应到达时,处理函数被调用。 处理函数会被多次调用,每次请求状态发生变化时(例如标头已到达或请求已完成)都会被调用。

下面是一个简短的示例:

function request() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
            print('HEADERS_RECEIVED');
        } else if(xhr.readyState === XMLHttpRequest.DONE) {
            print('DONE');
        }
    }
    xhr.open("GET", "http://example.com");
    xhr.send();
}

对于响应,可以获取XML格式内容,或仅获取原始文本格式。 可以迭代生成的XML;但现在,更常用的是原始文本格式,可以用于JSON格式的响应。 JSON文档将使用JSON.parse(text)将文本转换为JS对象。

/* ... */
} else if(xhr.readyState === XMLHttpRequest.DONE) {
    var object = JSON.parse(xhr.responseText.toString());
    print(JSON.stringify(object, null, 2));
}

在响应处理器中,访问原始响应文本并将其转换为javascript对象。 这个JSON对象现在是一个有效的JS对象(在javascript中,一个js对象可以是一个对象或一个数组)。

提示

似乎首先使用toString()转换会使代码更稳定。 如果没有这种显式转换,作者发现过几次解析器错误。 但是并不确定是什么原因造成的。

# Flickr调用

看一个更真实的例子。 一个典型的例子是使用Flickr服务来检索新上传图片的公共提要(Feed)。 为此,可以使用http://api.flickr.com/services/feeds/photos_public.gneURL。 不幸的是,它默认返回的是一个XML流,可以很容易地被qml中的XmlListModel解析;为了示例的目的,希望专注于JSON数据。 为了获得的是一个干净的JSON响应,需要在请求中附加一些参数:http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1,这将返回一个没有JSON回调的JSON响应。

提示

JSON回调将JSON响应包装到函数调用中。 这是HTML编程中使用的快捷方式,其中脚本标签(tag)用于发出JSON请求。 响应将触发回调定义的本地函数。 QML中没有适用于JSON回调的机制。

首先使用curl检查响应:

curl "http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1&tags=munich"

响应将如下这样:

{
    "title": "Recent Uploads tagged munich",
    ...
    "items": [
        {
        "title": "Candle lit dinner in Munich",
        "media": {"m":"http://farm8.staticflickr.com/7313/11444882743_2f5f87169f_m.jpg"},
        ...
        },{
        "title": "Munich after sunset: a train full of \"must haves\" =",
        "media": {"m":"http://farm8.staticflickr.com/7394/11443414206_a462c80e83_m.jpg"},
        ...
        }
    ]
    ...
}

返回的JSON文档具有已定义的结构,是具有标题(title)和项目(items)属性的对象。 其中,title是一个字符串,items是一个对象数组。 将此文本转换为JSON文档时,可以访问各个条目,因为它是一个有效的JS对象/数组结构。

// JS code
obj = JSON.parse(response);
print(obj.title) // => "Recent Uploads tagged munich"
for(var i=0; i<obj.items.length; i++) {
    // iterate of the items array entries
    print(obj.items[i].title) // title of picture
    print(obj.items[i].media.m) // url of thumbnail
}

作为一个有效的JS数组,可以使用obj.items数组作为列表视图的模型。 现在将努力实现这一目标。 首先,需要检索响应并将其转换为有效的JS对象;然后,可以将response.items属性设置为列表视图的模型。

function request() {
    const xhr = new XMLHttpRequest()
    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
            print('HEADERS_RECEIVED')
        } else if(xhr.readyState === XMLHttpRequest.DONE) {
            print('DONE')
            const response = JSON.parse(xhr.responseText.toString())
            // Set JS object as model for listview
            view.model = response.items
        }
    }
    xhr.open("GET", "http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1&tags=munich")
    xhr.send()
}

这是完整的源代码。在加载组件时创建请求,然后将请求响应作为简单列表视图的模型。

import QtQuick

Rectangle {
    id: root

    width: 320
    height: 480

    ListView {
        id: view
        anchors.fill: parent
        delegate: Thumbnail {
            required property var modelData
            width: view.width
            text: modelData.title
            iconSource: modelData.media.m
        }
    }

    function request() {
        const xhr = new XMLHttpRequest()
        xhr.onreadystatechange = function() {
            if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
                print('HEADERS_RECEIVED')
            } else if(xhr.readyState === XMLHttpRequest.DONE) {
                print('DONE')
                const response = JSON.parse(xhr.responseText.toString())
                // Set JS object as model for listview
                view.model = response.items
            }
        }
        xhr.open("GET", "http://api.flickr.com/services/feeds/photos_public.gne?format=json&nojsoncallback=1&tags=munich")
        xhr.send()
    }

    Component.onCompleted: {
        root.request()
    }
}

当文档完全加载时(Component.onCompleted),从Flickr请求最新的提要(Feed)内容。 结果到达后,解析JSON响应并将items数组设置为视图的模型。 列表视图有一个代理,它在一行中显示缩略图图标和标题文本。

另一种选择是使用占位符ListModel并将每个项附加到列表模型上。 为了支持更大的模型,需要支持分页(例如第 1 页,共 10 页)和惰性内容检索。

最后更新: 1/23/2022, 11:46:02 PM