具有Free Pascal的TIdHTTPServer的UTF-8响应

时间:2013-04-11 13:23:02

标签: delphi http indy freepascal indy10

此代码启动一个HTTP服务器,用于侦听端口8080上的请求。使用Delphi 2009编译时,中文文本正确呈现。但是,使用Free Pascal 2.6.0,浏览器会显示中文而不是中文

使用Indy和Free Pascal编写Unicode / UTF-8 HTTP响应的正确方法是什么?

program IdHTTPUnicode;

{$APPTYPE CONSOLE}

uses
  IdHTTPServer, IdCustomHTTPServer, IdContext, IdSocketHandle, IdGlobal,
  SysUtils;

type
  TMyServer = class (TIdHTTPServer)
  public
    procedure InitComponent; override;
    procedure DoCommandGet(AContext: TIdContext;
      ARequestInfo: TIdHTTPRequestInfo;
      AResponseInfo: TIdHTTPResponseInfo); override;
  end;

procedure Demo;
var
  Server: TMyServer;
begin
  Server := TMyServer.Create(nil);
  try
    try
      Server.Active := True;
    except
      on E: Exception do
      begin
        WriteLn(E.ClassName + ' ' + E.Message);
      end;
    end;
    WriteLn('Hit any key to terminate.');
    ReadLn;
  finally
    Server.Free;
  end;
end;

procedure TMyServer.InitComponent;
var
  Binding: TIdSocketHandle;
begin
  inherited;

  Bindings.Clear;
  Binding := Bindings.Add;
  Binding.IP := '127.0.0.1';
  Binding.Port := 8080;
  Binding.IPVersion := Id_IPv4;
end;

procedure TMyServer.DoCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
const
  UNI = '中文';
begin
  AResponseInfo.ContentText := '<html>' + UNI + '</html>';
  AResponseInfo.ContentType := 'text/html';
  AResponseInfo.CharSet := 'UTF-8';
end;

begin
  Demo;
end.

在调试器中,我可以看到执行方法TIdIOHandler.Write中的不同代码,对于Free Pascal,定义了STRING_IS_ANSI:

procedure TIdIOHandler.Write(const AOut: string; AByteEncoding: TIdTextEncoding = nil
  {$IFDEF STRING_IS_ANSI}; ASrcEncoding: TIdTextEncoding = nil{$ENDIF}
  );
begin
  if AOut <> '' then begin
    AByteEncoding := iif(AByteEncoding, FDefStringEncoding);
    {$IFDEF STRING_IS_ANSI}
    ASrcEncoding := iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault);
    {$ENDIF}
    Write(
      ToBytes(AOut, -1, 1, AByteEncoding
        {$IFDEF STRING_IS_ANSI}, ASrcEncoding{$ENDIF}
        )
      );
  end;
end; 

2 个答案:

答案 0 :(得分:5)

FreePascal字符串不像Delphi 2009+那样使用UTF-16编码。在FreePascal和Delphi 2007及更早版本中,您的代码需要考虑实际的字符串编码。这就是为什么Indy为这些平台公开了额外的基于Ansi的参数/属性。

TIdHTTPServer使用ContentText写出TIdIOHandler.Write()时,ASrcEncoding参数不会在非Unicode平台上使用,因此您必须使用{{1}相反,让TIdIOHandler.DefAnsiEncoding知道Write()的编码是什么,例如:

ContentText

或者更一般地说:

procedure TMyServer.DoCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
const
  UNI: WideString = '中文';
begin
  AResponseInfo.ContentText := UTF8Encode('<html>' + UNI + '</html>');
  AResponseInfo.ContentType := 'text/html';

  // this tells TIdHTTPServer what to encode bytes to during socket transmission
  AResponseInfo.CharSet := 'utf-8';

  // this tells TIdHTTPServer what encoding the ContentText is using
  // so it can be decoded to Unicode prior to then being charset-encoded
  // for output. If the input and output encodings are the same, the
  // Ansi string data gets transmitted as-is without decoding/reencoding...
  AContext.Connection.IOHandler.DefAnsiEncoding := IndyUTF8Encoding;
end;

答案 1 :(得分:0)

现代FreePascal字符串默认为UTF-8,除非您调整了编译器选项。

因此iif(ASrcEncoding, FDefAnsiEncoding, encOSDefault);似乎encOSDefault的值是错误的。 如果你愿意,你可以在INDY来源中修复它的检测,或者我想更好的是设置DefAnsiEncoding := 'utf-8';(RFC AFAIR的低例)

为了安全起见,您可以在程序开始时检查UTF-8模式。设置一些非拉丁常数(如中国的东西,或希腊语或西里尔语 - 无论如何)并检查它是否是UTF8:http://compaspascal.blogspot.ru/2009/03/utf-8-automatic-detection.html

但总的来说,我认为你可能会尝试找到一些比Indy更关心FPC和Linux的库。 Indy在我看来停滞不前,甚至在Delphi上也被抛弃了。也许Synopse mORMot(查找DataSnap性能测试文章)可以帮助您或CodeTyphon发行版附带的某些库。