6

I am trying to use an API to update an online database.

The API is hosted by a french site and the usage details are slim with no examples.

This is the information they have provided

The query must be sent as a "multipart / form-data" html form using the "POST" method. 
Input parameters: 
ssid : (type "text") user's ScreenScraper identifier 
sspassword : (type "text") ScreenScraper password of the user 
gameid : (type "text") numerical identifier of the game on ScreenScraper 
or 
romid ( "text") numeric ID of the Roma on ScreenScraper 

to propose a textual info: 
modiftypeinfo ( "text") type of information sent (see list "modiftypeinfo") 
modifregion ( "text"





<html>
  <head>
  <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
  <META http-equiv="content-language" content="fr"> 
  <title></title>
  </head> 
  <body> 
    <form name="myform" id="myform" method="post" enctype="multipart/form-data" action="https://www.screenscraper.fr/api/botProposition.php?ssid=xxx&sspassword=yyy"> 
      <input type="text" name="gameid" value="3"> 
      <input type="text" name="modiftypeinfo" value="description">
      <input type="text" name="modifregion" value="">   
      <input type="text" name="modiflangue" value="fr"> 
      <input type="text" name="modifversion" value=""> 
      <input type="text" name="modiftexte" value="ici, le synopsis du jeu ! ne pas valider, c'est un test ;)"> 
      <input type="text" name="modifsource" value="botProposition / source de l'info">
      <input type="submit" value="envoyer"> 
    </form>

  <script> 
    document.getElementById("myform").submit();
    </script>

  </body>
</html> 

This is the code I have put together:

Sub Post_Detail()

    Dim xmlhttp As Object, response As String, SendString As String

    Set xmlhttp = CreateObject("MSXML2.XMLHTTP.6.0")

    '~~> Indicates that page that will receive the request and the type of request being submitted
    xmlhttp.Open "POST", "https://www.screenscraper.fr/api/botProposition.php?ssid=xxx&sspassword=yyy", False

    '~~> Indicate that the body of the request contains form data
    xmlhttp.setRequestHeader "Content-Type", "text/html; charset=UTF-8"
    xmlhttp.setRequestHeader "enctype", "multipart/form-data"

    '~~> Send the data as name/value pairs
    SendString = "&type=text&name=gameid&value=185882"
    SendString = SendString & "&type=text&name=modiftypeinfo&value=genres"
    SendString = SendString & "&type=text&name=modiftexte&value=Sports"
    SendString = SendString & "&type=submit&value=envoyer"
    Debug.Print SendString
    xmlhttp.send SendString

    response = xmlhttp.responseText
    MsgBox response

    Set xmlhttp = Nothing

End Sub

I am not sure what to do with this part: form name="myform" id="myform" in the instructions and I am not sure if my request via building a string is even anywhere near correct.

You won't be able to run without a login, it will return a password invalid error.

Dan Donoghue
  • 6,056
  • 2
  • 18
  • 36
  • The name and id of the HTML form don't get sent with the submission, so you can leave those out. The only thing you need is the name and the value of the inputs (so also leave off the "type=text"). If you have a page with that form you can use (eg) Fiddler or your browser's Developer tools to view what get sent when you submit the form. – Tim Williams Mar 31 '17 at 01:03

2 Answers2

10

The structure of multipart/form-data is described in RFC7578. There is a sample:

POST /form.html HTTP/1.1
Host: server.com
Referer: http://server.com/form.html
User-Agent: Mozilla
Content-Type: multipart/form-data; boundary=-------------573cf973d5228
Content-Length: 288
Connection: keep-alive
Keep-Alive: 300


---------------573cf973d5228
Content-Disposition: form-data; name="field"

text
---------------573cf973d5228
Content-Disposition: form-data; name="file"; filename="sample.txt"
Content-Type: text/plain

Content file
---------------573cf973d5228--

Try the below code to make POST XHR:

Sub POST_multipart_form_data()
    
    Dim oFields As Object
    Dim sBoundary As String
    Dim sPayLoad As String
    Dim sName As Variant
    
    Set oFields = CreateObject("Scripting.Dictionary")
    With oFields
        .Add "gameid", "3"
        .Add "modiftypeinfo", "description"
        .Add "modifregion", ""
        .Add "modiflangue", "fr"
        .Add "modifversion", ""
        .Add "modiftexte", "ici, le synopsis du jeu ! ne pas valider, c'est un test ;)"
        .Add "modifsource", "botProposition / source de l'info"
    End With
    sBoundary = String(6, "-") & Replace(Mid(CreateObject("Scriptlet.TypeLib").GUID, 2, 36), "-", "")
    sPayLoad = ""
    For Each sName In oFields
        sPayLoad = sPayLoad & "--" & sBoundary & vbCrLf
        sPayLoad = sPayLoad & "Content-Disposition: form-data; name=""" & sName & """" & vbCrLf & vbCrLf
        sPayLoad = sPayLoad & oFields(sName) & vbCrLf
    Next
    sPayLoad = sPayLoad & "--" & sBoundary & "--"
    With CreateObject("MSXML2.XMLHTTP")
        .Open "POST", "https://www.screenscraper.fr/api/botProposition.php?ssid=xxx&sspassword=yyy", False
        .setRequestHeader "Content-Type", "multipart/form-data; boundary=" & sBoundary
        .setRequestHeader "Content-Length", LenB(sPayLoad)
        .send (sPayLoad)
        MsgBox .responseText ' Erreur de login : Verifier vos identifiants !
    End With
    
End Sub

So the payload data generated by the code is as follows:

--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="gameid"

3
--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modiftypeinfo"

description
--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modifregion"


--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modiflangue"

fr
--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modifversion"


--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modiftexte"

ici, le synopsis du jeu ! ne pas valider, c'est un test ;)
--------CFE02291483D4B3EB3B1A92257838D94
Content-Disposition: form-data; name="modifsource"

botProposition / source de l'info
--------CFE02291483D4B3EB3B1A92257838D94--

Note that optional "Content-Type" header field for each part defaults to "text/plain". As you can see in comment there is login error in response for me:

Erreur de login : Verifier vos identifiants !

The only thing I can't get is why they specified the submission as a multipart/form-data? Even there are no files attached! It's much more easier to send the data as application/x-www-form-urlencoded using the following code:

Sub POST_application_x_www_form_urlencoded()
    
    Dim oFields As Object
    Dim sPayLoad As String
    Dim sName As Variant
    
    Set oFields = CreateObject("Scripting.Dictionary")
    With oFields
        .Add "gameid", "3"
        .Add "modiftypeinfo", "description"
        .Add "modifregion", ""
        .Add "modiflangue", "fr"
        .Add "modifversion", ""
        .Add "modiftexte", "ici, le synopsis du jeu ! ne pas valider, c'est un test ;)"
        .Add "modifsource", "botProposition / source de l'info"
    End With
    For Each sName In oFields
        oFields(sName) = sName & "=" & EncodeUriComponent(oFields(sName))
    Next
    sPayLoad = Join(oFields.Items(), "&")
    With CreateObject("MSXML2.XMLHTTP")
        .Open "POST", "https://www.screenscraper.fr/api/botProposition.php?ssid=xxx&sspassword=yyy", False
        .setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        .setRequestHeader "Content-Length", LenB(sPayLoad)
        .send (sPayLoad)
        MsgBox .responseText ' Erreur de login : Verifier vos identifiants !
    End With
    
End Sub

Function EncodeUriComponent(sText As String) As String
    Static oHtmlfile As Object
        
    If oHtmlfile Is Nothing Then
        Set oHtmlfile = CreateObject("htmlfile")
        oHtmlfile.parentWindow.execScript "function encode(s) {return encodeURIComponent(s)}", "jscript"
    End If
    EncodeUriComponent = oHtmlfile.parentWindow.encode(sText)
End Function

And the payload data generated by the code is as follows:

gameid=3&modiftypeinfo=description&modifregion=&modiflangue=fr&modifversion=&modiftexte=ici%2C%20le%20synopsis%20du%20jeu%20!%20ne%20pas%20valider%2C%20c'est%20un%20test%20%3B)&modifsource=botProposition%20%2F%20source%20de%20l'info

The error response is the same as for the first method for me.

Community
  • 1
  • 1
omegastripes
  • 12,351
  • 4
  • 45
  • 96
  • OMG I had given up on this, thank you so much the multipart form data is working, the reason it is multipart is so you can submit a file along with it if need be (uploading artwork). – Dan Donoghue Apr 06 '17 at 22:43
1

Untested:

Sub Post_Detail()

    Dim xmlhttp As Object, response As String, SendString As String

    Set xmlhttp = CreateObject("MSXML2.XMLHTTP.6.0")

    '~~> Indicates that page that will receive the request and the type of request being submitted
    xmlhttp.Open "POST", "https://www.screenscraper.fr/api/botProposition.php?ssid=xxx&sspassword=yyy", False

    '~~> Indicate that the body of the request contains form data
    xmlhttp.setRequestHeader "enctype", "multipart/form-data"

    '~~> Send the data as name/value pairs
    SendString = "gameid=185882" & _
                "&modiftypeinfo=genres" & _
                "&modiftexte=Sports"

    Debug.Print SendString
    xmlhttp.send SendString

    response = xmlhttp.responseText
    MsgBox response

    Set xmlhttp = Nothing

End Sub

I don't think the submit button is included in the POST if it has no name attribute.

Tim Williams
  • 154,628
  • 8
  • 97
  • 125
  • Thanks Tim, same issue. The debug output is now gameid=185882&modiftypeinfo=genres&modiftexte=Sports. Unfortunately the returned string isn't an error when there is an error, it's just blank which really makes it hard to analyse. – Dan Donoghue Mar 31 '17 at 01:19
  • 1
    If you run Fiddler while performing the POST it might give you some clues as to what's happening. – Tim Williams Mar 31 '17 at 01:31
  • I ran fiddler and it didn't return any results when I ran the code. I can only assume something is stopping Fiddler from picking up HTTPS on my work machine. I will try it again when I get home. – Dan Donoghue Mar 31 '17 at 02:55