在没有原始数据的情况下重新发送post / put请求

时间:2014-06-23 15:20:39

标签: c++ qt qtnetwork

我有一个最近一直困扰我的问题。我正在创建一个简单的Web浏览器,以便与单个网站进行交互,以便用户管理产品。我目前遇到的问题是:用户可以使用基本Web表单通过ui上传新文件。选择上传后,请求将失败,错误代码为205,即:

QNetworkReply :: ContentReSendError - 需要再次发送请求,但此失败,例如因为无法再次读取上传数据。

如果用户再次尝试上传,该文件将上传而不会出错。要解决此问题,我想检测是否有错误,如果是,请重新发送请求。

我的问题是我没有数据与请求一致。我可以检索请求本身,但不能检索数据。我想我的解决方案看起来像this stack overflow question:

中的嵌套
if((reply->error() == QNetworkReply::TemporaryNetworkFailureError) || (reply->error() == QNetworkReply::ContentReSendError)) {
     // retry the last request
     _reply = accessManager.put(reply->request(), _currentItem);
}

但是,此用户可以访问_currentItem,因为他们最初在他们的程序中创建了请求,而我的动态正在创建。有什么方法可以捕获正在发送的数据或缓存它吗?我原本希望QNetworkAccessManager存储以前的请求以便轻松重试,但我没有看到类似的东西。感谢您的任何见解和帮助!

修改

为了澄清:无论如何都使用QNetworkAccessManager来获取未在程序中定义的帖子/放置的请求和数据。

2 个答案:

答案 0 :(得分:1)

是的,有两种方法,但因为该功能不存在。

  1. 编辑Qt的源代码,将其直接添加到QNetworkAccessManager

  2. 将其实现为在同一进程中运行的仅缓存HTTP代理。代理的实现可以使用嵌套的QNetworkAccessManager来执行真实网络上的请求。实现需要解码传入的请求,重新构建QNetworkRequest标头,并将其与任何请求的数据一起传递给嵌套管理器。此时,您可以预留数据,并在代理级别实施重试。

  3. 实施选项1可能会减少工作量和麻烦,但有些项目非常反对修改Qt,所以我会在这种情况下选择第2项。

答案 1 :(得分:0)

使用Python和PyQt,我通过使用函数装饰器解决了类似的问题,该函数装饰器将请求保存在lambda函数中,如果失败则重新发送。也许在C ++中可能有类似的结构。

在我的特定情况下,应用程序必须使用OAuth2系统,该系统发出的令牌只有一个小时有效(并且无法刷新;隐式授权)。因此,在这一段时间之后,用户可能会突然对他的一个请求进行“未经过身份验证”的响应,这当然不是真正用户友好的。所以我想,我会抓住这个401响应,让用户重新进行身份验证,然后重新发送失败的最后一个请求。

在我的QNetworkAccessManager的子类中,我用一个名为requires_internet_connection的函数修饰了我的HTTP函数(get,put,post等),所以我的get函数就像是

Route::resource('item', 'ItemController');

装饰者看起来如下:

 @check_network_accessibility
 def get(self, url, callback, *args, **kwargs):
    """ Perform a HTTP GET request """
    ...

总结一下,我检查用户是否已登录,然后我将完全使用传递的参数存储要发送的请求,并将其存储在lambda函数中,并将其存储在pending_requests字典中,并使用唯一的uuid作为键。我将这个唯一的uuid注入要调用的函数的kwargs(使用键_request_id),这样如果请求成功,我可以使用此uuid将其从包含待处理请求的字典中删除(换句话说:干净)向上)。您可以在QNetworkReply的回调函数中执行此操作,如果它完成没有问题。

如果请求不成功,则会向用户显示登录窗口,如果他或她再次成功登录,则会调用一个函数来遍历待处理请求的字典并尝试再次执行:

def check_network_accessibility(func):
    """ Decorator function, not to be called directly.
    Buffers the network request so that it can be sent again if it fails the first time, due to an invalidated 
    OAuth2 token. In this case the user will be presented with the login 
    screen again. If the *same* user successfully logs in again, the request 
    will be resent. """

    @wraps(func)
    def func_wrapper(inst, *args, **kwargs):
        if inst.logged_in_user:
            # Create an internal ID for this request
            request_id=uuid.uuid4()
            current_request = lambda: func(inst, *args, **kwargs)
            # Add tuple with current user, and request to be performed
            # to the pending request dictionary
            inst.pending_requests[request_id] = (
                inst.logged_in_user['data']['id'], 
                current_request)
            # Add current request id to kwargs of function being called
            kwargs['_request_id'] = request_id
        return func(inst, *args, **kwargs)
    return func_wrapper

我使用字典的原因是可以在Qt中并行发送多个请求,当用户必须重新进行身份验证时,它们当然都会失败。我想让所有人在同一用户再次登录后再次尝试。

正如我之前所说,我知道这是Python与C ++完全不同(我几乎不知道任何事情),但我希望它能很好地说明原理,让人们想到C ++中的类似结构