Search code examples
delphiutf-8indydelphi-xe7indy10

IdHTTPServer and IdHTTP with Encoding UTF8


I am testing a localhost server using TIdHTTPServer and TIdHTTP. I am having problems with encoding UTF8 data.

client side:

procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  res: string;
begin
  res:=IdHTTP1.Get('http://localhost/?msg=đi chơi thôi');
  Memo1.Lines.Add(res);
end;

Server side:

procedure TForm1.OnCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  Memo1.Lines.Add(ARequestInfo.Params.Values['msg']); // ?i ch?i th?i

  AResponseInfo.CharSet := 'utf-8';
  AResponseInfo.ContentText := 'chào các bạn'; // chào các b?n
end;

I want to send đi chơi thôi and receive chào các bạn. But the server receives ?i ch?i th?i and the client receives chào các b?n.

Can anyone help me?


Solution

  • TIdHTTP transmits the URL exactly as you give it, but http://localhost/?msg=đi chơi thôi is not a valid URL that can be transmitted as-is, as URLs can only contain ASCII characters. Unreserved ASCII characters can be used as-is, but reserved and non-ASCII characters MUST be charset-encoded into bytes and then those bytes must be url-encoded in %HH format, eg:

    IdHTTP1.Get('http://localhost/?msg=%C4%91i%20ch%C6%A1i%20th%C3%B4i');
    

    You must ensure you pass only valid url-encoded URLs to TIdHTTP.

    In this example, the URL is hard-coded, but if you need something more dynamic then use the TIdURI class, eg:

    IdHTTP1.Get('http://localhost/?msg=' + TIdURI.ParamsEncode('đi chơi thôi'));
    

    TIdHTTPServer will then decode the parameter data as you are expecting. Both TIdURI and TIdHTTPServer use UTF-8 by default.

    When sending a response, you are only setting a CharSet, but you are not setting a ContentType. So TIdHTTPServer will set the ContentType to 'text/html; charset=ISO-8859-1', overwriting your CharSet. You need to explicitly set the ContentType yourself so you can specify a custom CharSet, eg:

    AResponseInfo.ContentType := 'text/plain';
    AResponseInfo.CharSet := 'utf-8';
    AResponseInfo.ContentText := 'chào các bạn';
    

    Or:

    AResponseInfo.ContentType := 'text/plain; charset=utf-8';
    AResponseInfo.ContentText := 'chào các bạn';
    

    On a side note, TIdHTTPServer is a multi-threaded component. The OnCommand... events are fired in the context of a worker thread, not the main UI thread. So accessing Memo1 directly like you are is not thread-safe. You MUST synchronize with the main UI thread in order to access UI controls safely, eg:

    procedure TForm1.OnCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
    var
      msg: string;
    begin
      msg := ARequestInfo.Params.Values['msg'];
      TThread.Synchronize(nil,
        procedure
        begin
          Memo1.Lines.Add(msg);
        end
      );
      ...
    end;