Results 1 to 11 of 11

Thread: tweemaal GET op verschillende servers werkt niet, in browser wel.

  1. #1

    tweemaal GET op verschillende servers werkt niet, in browser wel.

    Iets vreemds:
    Ik benader een aantal webservices vanuit de applicatie vie WinInet calls, via GET of POST, zonder, of met request data. Geen enkel probleem, normaliter, maar nu heb ik iets waar ik niet bij kan.
    Het gaat om twee nieuwe webservices. De URL's daarvoor haal ik van een eigen webservice op, die is compleet (protocol (http: of https de servernaam en het complete pad van het script wat uiteindelijk opgehaald moet worden) en die rafel ik uitelkaar zodat ik weet welke server benaderd moet worden (andere dan eigen), poort en uit te voeren script; daaraan kunnen parameters worden toegevoegd, (script?par1&par2...) of er wordt een JSON string opgebouwd die vervolgens als requestdata in de call naa httpsendrequest worden doorgegeven.
    Met één van die applicaties is geen enkel probleem: GET van de URL, POST van het script naar de betreffende server met de parameters in JSON requestdata. Geen probleem: dat werkt gewoon.
    De andere applicatie echter: GET van de URL, GET van het script, waaraan parameters zijn toegevoegd, naar de betreffende server om gegevens op te halen: Dat geeft dan status ERROR_FILE_NOT_FOUND (getlastError ==> 2). Als ik hetzelfde invoer in een browser krijg ik echter wel resultaat (en het goede).

    Als ik stapsgewijs de code doorloop, zie ik dat de eerste sessie (om de URL op te halen) inderdaad acties op de server uitvoert, het message window toont verschillende activiteit. Met de tweede call ook, als dat een POST command is. Get echter knalt er direct uit, zonder signaleringen.

    (delphi2007 onder WindowsXP).

  2. #2
    Meestal zal dat liggen aan het geen (of verkeerd) gebruik van UrlEncode van de parameters.

    Maar zonder precies te weten hoe je dit doet is daar verder weinig over te zeggen.

  3. #3
    URLEncode daarvan maakt geen verschil. Ook dan werkt het niet.

    Het rare is: het heeft gewerkt met exact dezelfde gegevens (en URL). Als ik exact dezelfde string die ik nu als serverscript aanlever, in een browers uitvoer, of met SOAPUI of POSTMan, werkt het wel en krijk ik de gevraagde gegevens.

    Serverscript is "berichten/index.php/api/berichtenAantallen/?serienr=11111&versie=11111&modules=12345678" dat geft me in beide gevallen een JSON antwoord:

    {"status":"OK","aantal":0,"spoed":0,"tekst":""}

    Wat ervóór gekomen is: GET van andere site: van een sccript met één parameter (GetURL/?option='code', dat levert de URL die ik dan aanvul met de parameters - tot de bovenstaande:
    ik krijg terug:

    http://(server)/berichten/index.php/...htenAantallen/
    en dat vul ik dan aan met de parameters tot de volledige URL. Al dan niet met URLEncode - het werkt niet meer.

    Overigens: de return status (2) betekent volgens de Microsoft documentatie dat offline werken is ingesteld en dat dit request niet in de cachce voorkomt. Maar "offline werken" is NIET aangevinkt, en cache is disabled...
    Last edited by WillemGrooters; 04-Dec-17 at 15:26.

  4. #4
    De uitleg is mij allemaal behoorlijk onduidelijk. (GET van andere site ??)
    Je hebt het ook over http of https.

    Zonder echt exacte voorbeelden is het een kwestie van gissen waar het probleem ligt.

    Het enige dat ik zo even 123 kan bedenken is dat je met je eigen programma met die andere server https aanspreekt en je voor je programma geen openssl libraries hebt staan. Dat zou dan ook verklaren dat het in de browser wel goed gaat.

    Als het dan nog niet werkt en je kunt geen voorbeelden aandragen dan kun je nog met Wireshark kijken wat het verschil is tussen jouw aanroep en die van de browser.

  5. #5
    Zo moeilijk is dat toch niet?

    1. GET http://site1/getredirect?option=aantal
    dit geeft http://site2/berichten/index.php/api/berichtenAantallen
    2. GEThttp://site2/berichten/index.php/api/berichtenAantallen?key1=val1&key2=val2
    Dat zou moeten geven:
    {"status":"ok", "aantal":"(value)"}

    maar geeft waarde 2 op getlasterror...
    Vervang ik "&" door "%26" - war URLEncode ook doet - gebeurt hetzelfde.

    Ik gebruik de "standaard" werkwijze met WinInet voor beide (komt gewoon van Microsoft..)

    Sessie := Createsession
    Connectie := OpenConnection (sessie, pointer naar servernaam, port=80)
    Request := OpenRqeuest (Connectie, GET. pointre naar script met parameters en lengte; cache disabled)
    SendRequest (Request, pointer naar contenttype, lengte contentype, pointre naar requeststring (die leeg is) en lengte ervan (dus 0))
    dwError := getlasterror
    case dwerror of:
    SUCCES: Haal data op en verwerkt dat
    ...
    end; { case }

    Close (request)
    Close (Connectie)
    Close (Sessie);

    Dit gebruik ik op alle 'directe' benadering van services (de meeste hebben wel degelijk requestdata, JSON formaat) - en nergens heb ik dit probleem.

    Overigens: Net gevonden dat het met de indy-interface WEL lukt....Maar waarom dan niet met WinInet?
    Last edited by WillemGrooters; 06-Dec-17 at 18:29.

  6. #6
    Wat ik altijd doe in dit soort gevallen is de boel uitvoeren in Fiddler. Fiddler is een gratis web debugger en ik vind die ideaal om dit soort API's te testen, vooral door de inspectors die het result kunnen weergeven.

    Geef anders eens de URL en parameters voor je 2 sites zodat wij een test kunnen doen en je dan mogelijk verder kunnen helpen.

  7. #7
    Waarom niet gewoon de echte code doorgeven?

    "pointre naar requeststring" lijkt mij niet echt in jouw sourcecode te staan.
    En soms moet je naar Windows gewoon nil doorgeven en geen "lege string".

    Aan pseudocode kunnen wij niet zien dat je een klein foutje in de code maakt.
    Het is dan ook handig de indy code te plaatsen die wel werkt.

  8. #8
    Goed punt Rik.

  9. #9
    de code:
    Code:
       httpSession:=InternetOpen(PChar(tGSNAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
      if Assigned(httpSession) then
      begin
         try
            httpConnect:=InternetConnect(httpSession, PAnsiChar(FServerName), FServerPort, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
            if Assigned(httpConnect) then
            begin
               try
                  httpRequest:=HttpOpenRequest(httpConnect, PChar(FCommand), PChar(FServerScript), nil, nil, nil,
                          INTERNET_FLAG_PRAGMA_NOCACHE or INTERNET_FLAG_RELOAD or INTERNET_FLAG_NO_CACHE_WRITE or FSecure, 0);
                  if Assigned(httpRequest) then
                  begin
                     try
    		    // Zet opties: security, timeout limits
                        dwSize:=SizeOf(dwFlags);
                        if (InternetQueryOption(httpRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
                        begin
                           dwFlags:=dwFlags or SECURITY_FLAG_IGNORE_UNKNOWN_CA or SECURITY_FLAG_IGNORE_CERT_CN_INVALID or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
                           if not(InternetSetOption(httpRequest, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
                           begin
                             // Error afhandeling
                           end;
                          dwSize := Sizeof (vTimeout) ;
                           vTimeOut := cTimeOut;
                           if not ((InternetSetOption(httpRequest, INTERNET_OPTION_CONNECT_TIMEOUT, @vTimeout, dwSize)) and
                                   (InternetSetOption(httpRequest, INTERNET_OPTION_SEND_TIMEOUT, @vTimeout, dwSize)) and
                                   (InternetSetOption(httpRequest, INTERNET_OPTION_RECEIVE_TIMEOUT, @vTimeout, dwSize))) then
                           begin
                              // Error afhandeling
                           end;
                          InternetQueryOption(httpRequest, INTERNET_OPTION_CONNECT_TIMEOUT, @vTimeout, dwSize);
                          InternetQueryOption(httpRequest, INTERNET_OPTION_SEND_TIMEOUT, @vTimeout, dwSize);
                          InternetQueryOption(httpRequest, INTERNET_OPTION_RECEIVE_TIMEOUT, @vTimeout, dwSize);
                        end
                        else
                        begin
                           // Errro afhandeling
                        end;
                        // Nu het feitelijke werk
                        repeat
                        begin
                          HttpSendRequest(httpRequest, pChar(FContentType), Length(FContentType), PChar(Request), Length(Request));      // dit is de body!
    
                          dwError := getLastError;  //// <<=======================================================
    
                          if dwError <> ERROR_SUCCESS then
                            case dwError of
                              ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED : begin
    			 	// Error afhandeling
                                end;
                              end; {ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED }
                              ERROR_INTERNET_CANNOT_CONNECT, ERROR_INTERNET_NAME_NOT_RESOLVED : begin  // (WG1)
    			 	// Error afhandeling
                              end ;  {ERROR_INTERNET_CANNOT_CONNECT, ERROR_INTERNET_NAME_NOT_RESOLVED}
                              ERROR_FILE_NOT_FOUND: begin // (WG5)
    			 	// Error afhandeling
                              end;
                              else // Alle andere fouten
                              begin
    			 	// Error afhandeling
                              end;
                            end; {case}
                        end;
                        until (dwError = ERROR_SUCCESS) or (Fstatus <> iGSN_SUCCES);
    
                        if dwError = ERROR_SUCCESS then
                        begin
                           strmMem:=TMemoryStream.Create;
                           try
                              while InternetReadFile(httpRequest, @lpszBuff, SizeOf(lpszBuff), dwSize) do
                              begin
                                 if (dwSize = 0) then break;
                                 strmMem.Write(lpszBuff, dwSize);
                              end;
                           finally
                              antwoord := StreamToString(StrmMem) ;
                              // Free the stream
                              strmMem.Free;
                              if Antwoord <> '' then
                              begin
                                  Response := VerwerkAntwoord (Antwoord) ;
                              end ;
                           end;
                        end;
                     finally
                      begin
                        InternetCloseHandle(httpRequest);
                      end;
                    end;
                  end;
               finally
                begin
                  InternetCloseHandle(httpConnect);
                end;
               end;
            end else
            begin
    	  // Error afhandeling
            end;
         finally
          begin
            InternetCloseHandle(httpSession);
          end;
         end;
      end else
    ...
    Deze wordt in beide gevallen aangeroepen: alle variabele gegevens worden extern ingevuld en zijn correct - als gespecificeerd. Request is leeg, en de waarde van FContentType blijkt niet echt relevant; verschillende waarden zijn geprobeerd maar geen heeft effect gehad - evident omdat FCommand = 'GET' voor beide calls.

    Zo duidelijk?

    Met behulp van Fiddler ben ik er achter gekomen dat er wel degelijk een antwoord binnenkomt - dus de functie zelf doet het gewoon. het is alleen dat dwError (waar gemerkt) de waarde 2 (ERROR_FILE_NOT_FOUND) teruggeeft en niet 0 (ERROR_SUCCESS) waardoor de buffer niet ingelezen wordt. De oplossing is er dus wel: In dat geval ook de buffer lezen, en fout signaleren als die leeg is - want dan is er inderdaad wat aan de hand omdat er antwoord moet zijn.

    Enig idee waar die status 2 vandaan komt?
    Last edited by WillemGrooters; 07-Dec-17 at 11:34.

  10. #10
    Ok, zie aan code hebben we veel meer.

    Even daar wel even twee kleine opmerkingen over.
    Je hebt nogal geknipt en geplakt in deze code want de controle op ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED heeft een onjuist aantal end's.
    Maar ik neem aan dat dat komt omdat je te weinig (die extra end) weg hebt gehaald.

    Tweede opmerking is dat de code behoorlijk opgeschoond en gestroomlijnd zou kunnen worden. Je kunt bepaalde delen in subroutines zetten om het leesbaarder te maken en ook haal ik altijd met Ctrl+D de formatter eroverheen om goede indentation te krijgen.

    Dan het echte probleem:
    Delphi Code:
    1. HttpSendRequest(httpRequest, PChar(FContentType), Length(FContentType), PChar(Request), Length(Request)); // dit is de body!
    2. dwError := getLastError;
    Dit is fout.

    De documentatie zegt dit:
    Returns TRUE if successful, or FALSE otherwise. To get extended error information, call GetLastError.
    https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx

    Tevens zegt de GetLastError documentatie dit:
    Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. Multiple threads do not overwrite each other's last-error code.
    en
    Most functions that set the thread's last-error code set it when they fail. However, some functions also set the last-error code when they succeed. If the function is not documented to set the last-error code, the value returned by this function is simply the most recent last-error code to have been set; some functions set the last-error code to 0 on success and others do not.
    https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
    HttpSendRequest() zal dus een functie zijn die de last_error niet op 0 zet bij succes.

    Aangezien HttpSendRequest dus geen error geeft (want het resultaat zal wel TRUE zijn) krijgt GetLastError ook niet een 0 status.
    De GetLastError is de error die veel en veel eerder in je code voorgekomen is (en die wordt pas weer overschreven bij een ECHTE error).

    Eigenlijk moet je dus kijken of HttpSendRequest FALSE teruggekregen heeft en ALLEEN in dat geval de GetLastError controleren.

    Zoiets dus:
    Delphi Code:
    1. dwError := ERROR_SUCCESS;
    2. if not HttpSendRequest(httpRequest, PChar(FContentType), Length(FContentType), PChar(Request), Length(Request)) then // dit is de body!
    3.   dwError := getLastError;

  11. #11
    Dank - dat heb ik over het hoofd gezien. Werkt nu.

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •