mjn*_*mjn 2 delphi freepascal http indy 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.
Run Code Online (Sandbox Code Playgroud)
在调试器中,我可以看到执行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;
Run Code Online (Sandbox Code Playgroud)
FreePascal字符串不像在Delphi 2009+中那样使用UTF-16编码.在FreePascal和Delphi 2007及更早版本中,您的代码需要考虑实际的字符串编码.这就是Indy为这些平台公开其他基于Ansi的参数/属性的原因.
当TIdHTTPServer
写出ContentText
使用时TIdIOHandler.Write()
,该ASrcEncoding
参数不在非Unicode平台上使用,因此您必须使用该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;
Run Code Online (Sandbox Code Playgroud)
或者,更一般地说:
{$I IdCompilerDefines.inc}
procedure TMyServer.DoCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
const
UNI{$IFNDEF STRING_IS_UNICODE}: WideString{$ENDIF} = '??';
begin
{$IFDEF STRING_IS_UNICODE}
AResponseInfo.ContentText := '<html>' + UNI + '</html>';
{$ELSE}
AResponseInfo.ContentText := UTF8Encode('<html>' + UNI + '</html>');
{$ENDIF}
AResponseInfo.ContentType := 'text/html';
AResponseInfo.CharSet := 'utf-8';
{$IFNDEF STRING_IS_UNICODE}
AContext.Connection.IOHandler.DefAnsiEncoding := IndyUTF8Encoding;
{$ENDIF}
end;
Run Code Online (Sandbox Code Playgroud)