Автор Тема: [Сеть, интернет] Рекурсивный вызов функции запроса ответа от сервера через API  (Прочитано 393 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Latoid [?]

  • Новичок
  • *
  • Сообщений: 92
  • Репутация: 11
    • Награды
Здравствуйте.
Впервые в практике столкнулся  с необходимостью рекурсивного вызова функции - и встал на этом.
Вкратце:
Скрипт работает с API одной социальной сети.
Есть ф-ция _GetResponseFromdA, ее вызываем когда нужно послать запрос и получить ответ от сервера.

Ответ может содержать
в случае успеха:
запрашиваемые данные в JSON формате. В успешном ответе может содержаться ключ "status" со значением "success", а может и нет.
в случае неудачи:
либо JSON-ответ с обязательным ключом "status" и значением "error". В этом случае обязательно также будет ключ "error" и значением, содержащим тип ошибки.
Либо (что бывает часто, когда сервер перегружен) в ответ придет просто HTML страница:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">

<TITLE>ERROR: The request could not be satisfied</TITLE>

</HEAD><BODY>

<H1>ERROR</H1>

<H2>The request could not be satisfied.</H2>

<HR noshade size="1px">

CloudFront attempted to establish a connection with the origin, but either the attempt failed or the origin closed the connection.

<BR clear="all">

<HR noshade size="1px">

<PRE>

Generated by cloudfront (CloudFront)

Request ID:
</PRE>

<ADDRESS>

</ADDRESS>

</BODY></HTML>

Требуется:
1. Если получен успешный JSON ответ или JSON ответ с любой ошибкой кроме ошибки типа "user_api_threshold", вернуть ответ из функции.

2. Если получена HTML-страница или JSON ответ с ошибкой типа "user_api_threshold", поспать N секунд, затем вызывать _GetResponseFromdA из нее же самой до выполнения п.1 либо пока не исчерпается число попыток (скажем, 5)

Вот что есть на этот момент. Сходу сложно, наверное, будет разобраться, не придумал как упростить. Надеюсь, комментарии помогут.

Код: AutoIt [Выделить]
$iDelayPeriodWhenLimitExceededCounter = 0

Func _GetResponseFromdA ($sEndPoint, $sParams = "", $sMethod = "GET", $IsUsualPrefix = True)
    $sCurlString = Execute (_Pref("sCurlFirstPartOfString")) & ' "' & _Pref("sAPIHost") ; ф-ция _Pref берет настройки из базы данных, конкретно в этой строке формирует первую часть строки параметров для cUrl
;~  If $IsUsualPrefix = True Then $sCurlString &= _Pref("sRequestUsualPrefix")

    If $IsUsualPrefix = True Then
        $sCurlString &= _Pref("sRequestUsualPrefix") ; дальше формируем строку для cUrl
        $iSecondOfCheck = _TimeGetStamp()
        If $iSecondOfCheck - _GetInfoFromDataBase ("SELECT refresh_time FROM accounts WHERE id = '" & _Pref("iActiveAccount") & "';") > _Pref("iRefreshTokensPeriodinMin") * 60 Then ; если необходимый для API вызовов access_token устарел (выдан более 55 минут назад), надо его обновить
            ConsoleWrite ("надо рефрешить access_token" & @CRLF)
            $sParamsToRefreshTokens = "grant_type=refresh_token&client_id=" & _GetInfoFromDataBase ("SELECT client_id FROM accounts WHERE id = " & _Pref("iActiveAccount") & ";") & "&client_secret=" & _GetInfoFromDataBase ("SELECT client_secret FROM accounts WHERE id = " & _Pref("iActiveAccount") & ";") & "&refresh_token=" & _GetInfoFromDataBase ("SELECT refresh_token FROM accounts WHERE id = " & _Pref("iActiveAccount") & ";") ; формируем строку параметров для cUrl для обновления access_token
            $sRefreshResponse = _GetResponseFromdA ("token", $sParamsToRefreshTokens, "GET", False) ; обновляю access_token; первый случай рекурсии, с ней вроде справился, работает норм
            $sStatusOfRefreshResponse = _GetJSONValue ($sRefreshResponse, "status")
            If $sStatusOfRefreshResponse = "error" Then ; если в ответе обновлении access_token ошибка, то выходим
                Return -1
            ElseIf $sStatusOfRefreshResponse = "success" Then ; иначе пишем в БД новый access_token, refresh_token и время обновления
                _GetInfoFromDataBase ("UPDATE accounts SET access_token = " & _SQLite_Escape (_EncryptData(_GetJSONValue ($sRefreshResponse, "access_token"))) & " WHERE id = " & _Pref("iActiveAccount") & ";")
                _GetInfoFromDataBase ("UPDATE accounts SET refresh_token = " & _SQLite_Escape (_EncryptData(_GetJSONValue ($sRefreshResponse, "refresh_token"))) & " WHERE id = " & _Pref("iActiveAccount") & ";")
                _GetInfoFromDataBase ("UPDATE accounts SET refresh_time = " & _SQLite_Escape($iSecondOfCheck) & " WHERE id = " & _Pref("iActiveAccount") & ";")
            EndIf
        EndIf

        $sParams &= "&access_token=" & _GetInfoFromDataBase ("SELECT access_token FROM accounts WHERE id = '" & _Pref("iActiveAccount") & "';") ; дописываем к строке параметров cUrl рабочий access_token, взятый из БД
    EndIf

;~  Дописываем к строке параметров cUrl метод запроса - POST или GET
    $sCurlString &= _Pref("sRequestAuthPrefix") & $sEndPoint & '" -d "' & $sParams & '"' & ' -X '

    If $sMethod = "GET" Then
        $sCurlString &= 'GET -G'
    ElseIf $sMethod = "POST" Then
        $sCurlString &= 'POST'
    EndIf

    ; А вот это бы как-нибудь красиво зарекурсить
    $sAnswerFromdA = _AskdA ($sCurlString)
    $sStatusOfAnswer = _GetJSONValue ($sAnswerFromdA, "status")
    $sErrorType = ""
    If $sStatusOfAnswer = "error" Then $sErrorType = _GetJSONValue ($sStatusOfAnswer, "error")
    If StringRegExp ($sAnswerFromdA, "(?ms)^<\!DOCTYPE\sHTML.*<\/HTML>$") Or $sErrorType = "user_api_threshold" Then
        _WaitWhenLimitExceeded ()
        $sAnswerFromdA = _AskdA ($sCurlString)
    Else
        $iDelayPeriodWhenLimitExceededCounter = 0
    EndIf
    ; Конец А вот это бы как-нибудь красиво зарекурсить

    Return $sAnswerFromdA
EndFunc ; _GetResponseFromdA

Func _AskdA ($sStringForCurl)
    ; отпрвляю запрос с помощью cUrl
    $iPID = Run($sStringForCurl, '', @SW_HIDE, 6)
    ProcessWaitClose($iPID)
    $sResponseFromdA = StdoutRead($iPID)

;~  Если получена HTML страница или JSON -ответ с ошибкой, выводим MsgBox и пишем в лог
    If StringRegExp ($sResponseFromdA, "(?ms)^<\!DOCTYPE\sHTML.*<\/HTML>$") Or _GetJSONValue ($sResponseFromdA, "status") = "error" Then
        If _Pref("bShowErrorsFromdAMsgBoxes") = 1 Then MsgBox(16, "DevianArt вернул ошибку:", "Запрос: " & $sStringForCurl & @CRLF & @CRLF & "Ответ: " & $sResponseFromdA)
        If _Pref("bWriteErrorsInLog") = 1 Then
            $hErrorsLogFile = FileOpen (Execute (_Pref ("sErrorsLog")), 1)
            FileWrite ($hErrorsLogFile, "T: " & _Now () & @CRLF & "Q: " & $sStringForCurl & @CRLF & "A: " & $sResponseFromdA & @CRLF & "======================================" & @CRLF)
            FileClose ($hErrorsLogFile)
        EndIf
    EndIf
    Return $sResponseFromdA
EndFunc ; _AskdA

Func _WaitWhenLimitExceeded ()
    $iDelayPeriodWhenLimitExceededCounter += 1
    $iSecondsToSleep = _Pref("iDelayPeriodWhenLimitExceededStart") * _Pref("fpDelayPeriodWhenLimitExceededMultiplier") * $iDelayPeriodWhenLimitExceededCounter
    $ifSecondsElapsed = 0
    While 1
        $iSecondsRemained = Ceiling ($iSecondsToSleep - $ifSecondsElapsed)
;~      TrayTip ("Превышен лимит запросов", "Спим. Осталось " & $iSecondsRemained & " сек.", 2, 2)
        Sleep (1000)
        $ifSecondsElapsed += 1
        If $iSecondsRemained <= 0 Then ExitLoop
    WEnd
    If $iDelayPeriodWhenLimitExceededCounter >= _Pref("iDelayPeriodWhenLimitExceededMaxCount") Then $iDelayPeriodWhenLimitExceededCounter = 0
;~  TrayTip("", "", 0)
    Return
EndFunc ; _WaitWhenLimitExceeded


Русское сообщество AutoIt


Онлайн inververs [?]

  • AutoIt Гуру
  • *****
  • Сообщений: 2135
  • Репутация: 459
  • Пол: Мужской
    • Награды
  • Версия AutoIt: 3.3.12.0
Сделайте цикл от 1 до 5 вместо рекурсии

Оффлайн Latoid [?]

  • Новичок
  • *
  • Сообщений: 92

  • Автор темы
  • Репутация: 11
    • Награды
Сделайте цикл от 1 до 5 вместо рекурсии

И вот интересно, почему я сам в эту сторону не подумал?  :o
Ну да, проблема решается просто циклом. Просто встретилась рекурсивная по своей природе задача, и мозг даже не подумал в иную сторону. Наверное, хотелось разобраться, наконец, с рекурсией, чтоб повысить свой скил.

Онлайн inververs [?]

  • AutoIt Гуру
  • *****
  • Сообщений: 2135
  • Репутация: 459
  • Пол: Мужской
    • Награды
  • Версия AutoIt: 3.3.12.0
Повторение функции самой себя пока не будет ошибки - это не рекурсивная задача. И лучше обходится без нее там, где это возможно.

Русское сообщество AutoIt


Оффлайн Latoid [?]

  • Новичок
  • *
  • Сообщений: 92

  • Автор темы
  • Репутация: 11
    • Награды
inververs
Забыл поблагодарить вас за дельный совет. Спасибо  :)

Русское сообщество AutoIt


 

Похожие темы

  Тема / Автор Ответов Последний ответ
0 Ответов
2538 Просмотров
Последний ответ Декабрь 15, 2009, 13:49:48
от andrew_vvv
2 Ответов
3382 Просмотров
Последний ответ Июль 15, 2011, 19:00:46
от Garrett
6 Ответов
3443 Просмотров
Последний ответ Август 24, 2011, 18:09:14
от HungryDwarf
5 Ответов
7029 Просмотров
Последний ответ Июнь 07, 2012, 11:47:52
от ---Zak---
10 Ответов
4323 Просмотров
Последний ответ Август 23, 2012, 19:52:09
от Kaster
0 Ответов
1329 Просмотров
Последний ответ Август 23, 2012, 15:53:08
от madmasles
2 Ответов
2416 Просмотров
Последний ответ Февраль 02, 2013, 13:25:03
от Zaramot
2 Ответов
1891 Просмотров
Последний ответ Март 23, 2013, 21:34:18
от gregaz
11 Ответов
3234 Просмотров
Последний ответ Декабрь 23, 2015, 11:49:59
от ulan44
1 Ответов
1330 Просмотров
Последний ответ Декабрь 18, 2014, 15:35:43
от inververs