10

I am sure I'm doing something wrong here. I've followed every example I can find on stackoverflow and still haven't gotten this to work in my environment. I'd love to update my controls and environment, but I'm currently locked in with what I have.

I am using:

  • Delphi 7
  • Indy 10.0.52
  • ulkJSON.pas v1.07

I need to send this JSON to a URL:

"auth": {
    "applicationId": "appID",
    "applicationPassword": "pwd",
    "accountId": "acct",
    "userId": "dev"
}

There isn't anything terribly crazy about this, but when I try to post my request I tend to get a message that the request was Closed Gracefully. CheckIsReadable in IDSocketHandle.pas has Handleallocated = false. I'm not sure what I've done wrong in configuring my IdHTTP, but it just won't work.

I have tried examples from all these questions and several more, but none of these approaches seem to work for me:

Any tips would be greatly appreciated.

The current variant looks like this:

procedure Tformmaintestbed.btnJSONSendClick(Sender: TObject);
var
  code: Integer;
  sResponse: string;
  JsonToSend: TStringStream;
begin
  JsonToSend := TStringStream.Create(
    '{"auth": {"applicationId": "' + edApplication.text +
    '","applicationPassword": "' + edpassword.text +
    '","accountId": "' + edaccount.text +
    '","userId": "' + edUser.text +
    '"}}');
  try
    HTTP1.Request.ContentType := 'application/json';
    HTTP1.Request.ContentEncoding := 'utf-8';

    memoRequest.lines.clear;
    memoRequest.lines.add(JsonToSend);

    try
      sResponse := HTTP1.Post(cbAddress.text, JsonToSend);
    except
      on E: Exception do
        ShowMessage('Error on request: '#13#10 + e.Message);
    end;

    memoResponse.lines.clear;
    memoresponse.lines.add(sResponse);
  finally
    JsonToSend.Free();
  end;
end;

The idHTTP component is current set like this:

object HTTP1: TIdHTTP
  IOHandler = IdSSLIOHandlerSocketOpenSSL1
  AuthRetries = 0
  AuthProxyRetries = 0
  AllowCookies = True
  HandleRedirects = True
  ProxyParams.BasicAuthentication = False
  ProxyParams.ProxyPort = 0
  Request.ContentEncoding = 'utf-8'
  Request.ContentLength = -1
  Request.ContentRangeEnd = 0
  Request.ContentRangeStart = 0
  Request.ContentRangeInstanceLength = 0
  Request.ContentType = 'application/json'
  Request.Accept = 'application/json'
  Request.BasicAuthentication = False
  Request.UserAgent = 'Mozilla/3.0 (compatible; Indy Library)'
  HTTPOptions = [hoForceEncodeParams]
  Left = 564
  Top = 120
end
TLama
  • 71,521
  • 15
  • 192
  • 348
Jared Sherman
  • 119
  • 1
  • 2
  • 6
  • When the request has been sent successfully, what does "*just won't work*" mean then? What is it that you expect to happen? – JensG Jun 03 '14 at 22:18
  • 1
    1) JSON is just a text string: Tidhttp should work fine, 2) You should probably set ["content-type"](http://stackoverflow.com/questions/477816/what-is-the-correct-json-content-type) in the HTTP header to "text/json" or "application/json", 3) You should definitely use a tool like [Wireshark](http://www.wireshark.org/) or [Fiddler2](http://www.telerik.com/download/fiddler) to see if your JSON is getting sent, and how (or if) the server is responding. – FoggyDay Jun 03 '14 at 22:33
  • 2
    Please show the actual code that is not working for you. Don't just direct us to look at other people's examples. Show the code that YOU are actually using. You likely did not incorporate those examples into your project correctly. – Remy Lebeau Jun 03 '14 at 23:06
  • I set the content type to match the examples listed in the posts. There will be a response JSON message that I can parse to get a token. That token can then be used to make other calls to the site. With Fiddler running no messages showed up for me. Looks like I'm not even getting a message out. – Jared Sherman Jun 03 '14 at 23:09
  • If you want help with your code that isn't working, it's important that you **post the code that isn't working**. It's also hard to tell what you've done wrong with "configuring your IdHTTP" when you don't show how you're configuring it. If you want help here, you'll need to include the relevant information. It's pointless asking us to speculate on what might be wrong, when you can include what you're doing and we can try and specifically help you. Please [edit] your question and add the information there, where it can be properly formatted and people can easily find it. Thanks. – Ken White Jun 03 '14 at 23:10
  • There is a JSON parser in your list of used tools, but it is not used in the source code example. Using a JSON parser will avoid errors caused by malformed JSON. – mjn Jun 04 '14 at 10:34
  • A potential reason for unsuccessful post could be the browser agent string: http://www.indyproject.org/KB/index.html?iamgettinga403forbiddene.htm – mjn Jun 04 '14 at 10:36
  • Yes I removed the JSON parser to make sure it wasn't the cause of the problem. I intend to put it back once I can get a post to actually send out. I changed the user agent but it didn't fix the problem. – Jared Sherman Jun 04 '14 at 14:34

2 Answers2

10

HTTP1.Request.ContentEncoding should be HTTP1.Request.CharSet instead. UTF-8 is a charset encoding, not a content encoding. And then make sure your JSON data is actually encoded to UTF-8 before posting it. If you are using ASCII characters, the TStringStream code you showed is fine. But if you are using non-ASCII Characters, you need to encode them, such as with Utf8Encode(). TIdHTTP does not encode TStream data, it is sent as-is.

Procedure Tformmaintestbed.btnJSONSendClick(Sender: TObject);
var
  Json: string;
  sResponse: string;
  JsonToSend: TStringStream;
begin
  Json := '{"auth": {"applicationId": "' + edApplication.text +
    '","applicationPassword": "' + edpassword.text +
    '","accountId": "' + edaccount.text +
    '","userId": "' + edUser.text +
    '"}}';

  memoRequest.Text := Json;

  JsonToSend := TStringStream.Create(Utf8Encode(Json)); // D2007 and earlier only
  //in D2009 and later, use this instead:
  //JsonToSend := TStringStream.Create(Json, TEncoding.UTF8);
  try
    HTTP1.Request.ContentType := 'application/json';
    HTTP1.Request.CharSet := 'utf-8';

    try
      sResponse := HTTP1.Post(cbAddress.Text, JsonToSend);
    except
      on E: Exception do
        ShowMessage('Error on request: '#13#10 + e.Message);
    end;
  finally
    JsonToSend.Free;
  end;

  memoResponse.Text := sResponse;
end;

Alternatively:

Procedure Tformmaintestbed.btnJSONSendClick(Sender: TObject);
var
  Json: string;
  sResponse: string;
  JsonToSend: TMemoryStream;
begin
  Json := '{"auth": {"applicationId": "' + edApplication.text +
    '","applicationPassword": "' + edpassword.text +
    '","accountId": "' + edaccount.text +
    '","userId": "' + edUser.text +
    '"}}';

  memoRequest.Text := Json;

  JsonToSend := TMemoryStream.Create;
  try
    WriteStringToStream(JsonToSend, Json, enUTF8);
    JsonToSend.Position := 0;

    HTTP1.Request.ContentType := 'application/json';
    HTTP1.Request.CharSet := 'utf-8';

    try
      sResponse := HTTP1.Post(cbAddress.Text, JsonToSend);
    except
      on E: Exception do
        ShowMessage('Error on request: '#13#10 + e.Message);
    end;
  finally
    JsonToSend.Free;
  end;

  memoResponse.Text := sResponse;
end;
Remy Lebeau
  • 454,445
  • 28
  • 366
  • 620
  • It looks like my version of Indy is different from yours. I don't have a HTTP1.Request.CharSet property and if I don't set a a IOHandler then mine will get this: Error on request: IOHandler value is not valid – Jared Sherman Jun 04 '14 at 20:30
  • 10.0.52 is a VERY old version. The current version is 10.6.0. As for the `IOHandler` error, that only happens if you request an HTTPS url and do not have an SSL IOHandler assigned. That requirement is still true in the latest version. – Remy Lebeau Jun 04 '14 at 21:52
  • The `CharSet` property was added in 10.5.7 back in 2010. – Remy Lebeau Jun 04 '14 at 21:58
  • If I can convince my work to upgrade Indy do you know if any of the version have issues with Delphi 7? I see on their dev snapshot page that there are a few version of 10.6.0 Indy 10.6.0 - XE6 RTM/ Indy 10.6.0 - XE5 RTM/ Indy 10.6.0 - XE4 RTM/ Indy 10.5.8 - XE2 RTM/ Indy 10.5.7 - XE RTM/ Indy 10.5.5 - RS2010 RTM/ – Jared Sherman Jun 04 '14 at 23:12
  • Those are tags so we know which SVN revision of Indy was shipped with each major Delphi release in recent years. Indy itself still supports Delphi versions going all the way back to Delphi 5. – Remy Lebeau Jun 04 '14 at 23:36
  • Excellent I'll try it out and let you know. My fingers are crossed it works and I can convince them to upgrade. – Jared Sherman Jun 05 '14 at 05:58
  • We use TIDHash128 in Protocols\IDHash.pas. That no longer exists in 10.6.0. Is there another one that does the same? – Jared Sherman Jun 05 '14 at 17:44
  • `TIdHash128` was only being used with the `TIdMessageDigest...` classes (or did you implement your own 128bit hash?). Those classes still exist. If that is not what you are looking for, then please show your actual code that no longer works, and I will show the newer equivalent. – Remy Lebeau Jun 05 '14 at 18:31
  • function EncryptMD5(sString: String): String; begin with TIdHashMessageDigest5.Create do try result := UpperCase(TIdHash128.AsHex(HashValue(sString))); finally Free; end; end; This is from a different part of our code – Jared Sherman Jun 05 '14 at 18:53
  • `TIdHash` has various `Hash...AsHex()` methods, such as `HashStringAsHex()`, eg: `function EncryptMD5(sString: String): String; begin with TIdHashMessageDigest5.Create do try Result := UpperCase(HashStringAsHex(sString)); finally Free; end; end;` – Remy Lebeau Jun 05 '14 at 19:28
  • Also be aware that hashing a string is a charset-sensitive operation. The various `HashString...()` methods all have encoding parameters that you need to take into account if you are hashing non-ASCII characters. A hash operates on raw bytes, not on characters. A `String` has to be charset-encoded to a byte array before it can then be hashed, so you have to specify the charset to use for that encoding. Your code above does not do that, so Indy's default encoding (7bit ASCII by default) will be used, unless you specify otherwise. – Remy Lebeau Jun 05 '14 at 19:32
  • Ok, I'll look into that. I grabbed version 1.0.1.7 of OpenSSL from here: http://slproweb.com/products/Win32OpenSSL.html and it works. Thank you very much for your patience and help. I'll see what I can do to get around the HASH128 issue and see if I can push this as a solution for us. – Jared Sherman Jun 05 '14 at 19:39
1

Please try this:

procedure TForm1.Button1Click(Sender: TObject);
    var
        s: String;
        Resp_Json: string;
        Req_Json:TStream;
begin
    s:='state=1';
    s:=s+'&kind=0';
    s:=s+'&tblid=0';
    Req_Json:=TstringStream.Create(s);
    Req_Json.Position:=0;

    try
        IdHTTP1.Request.UserAgent:='Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36';
        IdHTTP1.Request.Accept := 'application/json, text/javascript, */*; q=0.01';
        IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded; charset=UTF-8';
        IdHTTP1.Request.CharSet:='utf-8';
        Resp_Json:=IdHTTP1.Post('http://[your URL]', Req_Json);
    finally
        Req_Json.Free;
    end;

    memo1.Lines.Add(IdHTTP1.ResponseText);
    memo1.Lines.Add(Resp_Json);
end;
Stepan Novikov
  • 1,387
  • 10
  • 22
asep
  • 11
  • 1