Используя Python с PyQt, я решил аналогичную проблему, используя декоратор функций, который сохраняет запрос в лямбда-функции и повторно отправляет его в случае сбоя. Возможно, аналогичная структура возможна в C++.
В моей конкретной ситуации приложение должно было работать с системой OAuth2, которая выдавала токены, которые были действительны только в течение одного часа (и не могли быть обновлены; неявное предоставление). Таким образом, может случиться так, что по прошествии этого часа пользователь внезапно получит ответ «Не аутентифицирован» на один из своих запросов, что, конечно, не очень удобно для пользователя. Поэтому я подумал, что перехватлю этот ответ 401, позволю пользователю повторно аутентифицироваться, а затем повторно отправлю последний неудачный запрос.
В моем подклассе QNetworkAccessManager я украсил свои HTTP-функции (get, put, post и т. д.) функцией с именем require_internet_connection, поэтому моя функция get, например, выглядит так:
@check_network_accessibility
def get(self, url, callback, *args, **kwargs):
""" Perform a HTTP GET request """
...
А декоратор выглядел так:
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
Подводя итог, я проверяю, залогинен ли пользователь, и только потом сохраняю запрос на отправку, полностью с переданными аргументами и всем остальным, в лямбда-функцию и сохраняю в словаре pending_requests с уникальным uuid в качестве ключа. Я ввожу этот уникальный uuid в kwargs (с ключом _request_id) вызываемой функции, чтобы в случае успешного выполнения запроса я мог использовать этот uuid для удаления его из словаря, содержащего ожидающие запросы (другими словами: чистый вверх). Вы можете сделать это в функции обратного вызова вашего QNetworkReply, если она завершится без проблем.
Однако, если запрос не выполнен успешно, пользователю предоставляется окно входа в систему, и если он или она снова успешно входит в систему, вызывается функция, которая просматривает словарь ожидающих запросов и пытается выполнить снова:
def handle_login(self):
""" Handles the login event received after login. """
self.get_logged_in_user(self.set_logged_in_user)
def set_logged_in_user(self, user_data):
""" Callback function for handle_login; get_logged_in_user"""
# Parse received data about logged in user
self.logged_in_user = json.loads(safe_decode(user_data.readAll().data()))
# If user had any pending requests from previous login, execute them now
for (user_id, request) in self.pending_requests.values():
if user_id == self.logged_in_user['data']['id']:
request()
self.pending_requests = {}
Причина, по которой я использую словарь, заключается в том, что в Qt можно отправлять несколько запросов параллельно, и, конечно, все они могут завершиться ошибкой, когда пользователю необходимо повторно аутентифицироваться. Я хочу, чтобы все они были опробованы снова после того, как тот же пользователь снова войдет в систему.
Как я уже говорил ранее, я знаю, что это Python, который работает совершенно иначе, чем C++ (о котором я почти ничего не знаю), но я надеюсь, что он достаточно хорошо иллюстрирует принцип, чтобы можно было представить аналогичную структуру в C++.
person
Daniel Schreij
schedule
25.04.2016