在TRestClient Delphi XE5中支持UTF-8编码字符串



我想使用DelphiXE5中的新TRest组件发送推文。我正在寻找一种UTF8编码我的推文的方法,其中包含IS0-8859-1字符。下面的代码有效,但涉及代码页转换等。是更好的方法吗?任何人

procedure TTwitterApi.Send(Tweet: string);
begin
  Reset;
  // Encode as UTF8 within (UTF-16 Delphi) string
  Tweet := EncodeAsUTF8(Tweet);
  FRestRequest.Resource := '1.1/statuses/update.json';
  FRestRequest.Method := rmPOST;
  FRestRequest.Params.AddItem('status', Tweet, pkGETorPOST);
  FRestRequest.Execute;
end;

function TTwitterApi.EncodeAsUTF8(UnicodeStr: string): string;
var
  UTF8Str: AnsiString;
  TempStr: RawByteString;
begin
  TempStr := UTF8Encode(UnicodeStr);
  SetLength(UTF8Str, Length(TempStr));
  Move(TempStr[1], UTF8Str[1], Length(UTF8Str));
  Result := UTF8Str;
end;

Twitter的1.1/statuses/update.json URL希望数据以application/x-www-form-urlencoded格式编码,因此需要将TRESTClient.ContentType属性设置为ctAPPLICATION_X_WWW_FORM_URLENCODED(默认情况下设置为ctNone)。

至于UTF-8,TRESTClient在内部使用Indy,Indy支持使用用户指定的字符集对出站数据进行编码,但Embarcadero似乎没有在其TRESTClient接口中添加该功能(不过,它确实处理响应中的字符集)。我不知道Embarcadero为什么会省略这样一个重要的功能。仅仅将字符串数据编码为UTF-8是不够的(BTW,你做得不对),但你还必须告诉Twitter数据已经进行了UTF-8编码(通过Content-Type REST标头的charset属性),据我所见,TRESTClient不允许你这样做。我不知道TRESTClient是否使用指定的默认字符集发送REST请求,但从它的来源来看,我认为它不会,但我还没有尝试过

至少,您需要修复EncodeAsUTF8()函数。它不会像您认为的那样生成一个包含UTF-8编码八位字节的UnicodeString。它生成一个UTF-8编码的AnsiString,然后使用RTL的默认Ansi代码页将其转换为UTF-16编码的UniodeString,因此您正在调用一个丢失UTF-8数据的数据转换。试试这个:

function TTwitterApi.EncodeAsUTF8(UnicodeStr: string): string;
var
  UTF8Str: UTF8String;
  I: Integer;
begin
  UTF8Str := UTF8String(UnicodeStr);
  SetLength(Result, Length(UTF8Str));
  for I := 1 to Length(UTF8Str) do
    Result[I] := Char(Ord(UTF8Str[I]));
end;

这至少应该允许TRESTClient在POST数据中对正确的UTF-8数据进行url编码。但是,您仍然需要处理Content-Type请求标头中缺少charset属性的问题(除非Twitter在未指定charset时默认为UTF-8)。

既然如此,如果你发现解决TRESTClient问题对你来说不可行,那么我建议改用Indy的TIdHTTP组件(它的application/x-www-form-urlencoded实现比TRESTClient使用的更准确),例如:

procedure TTwitterApi.Send(Tweet: string);
var
  Params: TStringList;
begin
  Reset;
  Params := TStringList.Create;
  try
    FParams.Add('status=' + Tweet);
    FIdHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
    FIdHTTP.Request.Charset := 'utf-8';
    FIdHTTP.Post('https://api.twitter.com/1.1/statuses/update.json', Params, IndyTextEncoding_UTF8);
  finally
    Params.Free;
  end;
end;

TRestRequest不适用于android,它会导致许多问题,特别是使用UTF8时,我无法解决,IdHttp Indy可以正常工作。

我已经用不同的API提供商(而不是Twitter)通过以下方式解决了这个问题:

function EncodeAsUTF8(UnicodeStr: string): AnsiString; // <-- Note the Ansi
var
  UTF8Str: UTF8String;
  I: Integer;
begin
  UTF8Str := UTF8String(UnicodeStr);
  SetLength(Result, Length(UTF8Str));
  for I := 1 to Length(UTF8Str) do
    Result[I] := AnsiChar(Ord(UTF8Str[I])); // <-- Note the Ansi
end;
...
fRESTClient1 := TRESTClient.Create(nil);
fRESTClient1.Accept := 'application/json';
fRESTClient1.AcceptCharset := 'UTF-8';
fRESTClient1.AcceptEncoding := 'identity';
fRESTClient1.ContentType := 'application/x-www-form-urlencoded';
...
rrOrder := TRESTRequest.Create(nil);
rrOrder.Accept := 'application/json';
rrOrder.AcceptCharset := 'UTF-8';
rrOrder.Client := fRESTClient1; {}
rrOrder.Method := rmPOST;
rrOrder.Resource := 'xxxxxx';
rrOrder.Params.AddItem('', EncodeAsUTF8(aJson), pkREQUESTBODY, [poDoNotEncode]);
rrOrder.Execute;

最新更新