无法使用 RestResponse 接收 contentType "application/pdf"

Cannot receive contentType "application/pdf" using RestResponse

我的应用程序使用 "application/json" 发送数据(个人信息)。如果数据有效,则服务器通过 "application/pdf" 向我发回 PDF 文件。但是当 RestResponse 到达时,会弹出一个异常:"No mapping for the unicode character exists in the target multibyte code page"。我不知道发生了什么。在过去的 4 天里,我一直在处理这个问题,但我无法解决这个问题。异常在行弹出:"RRequest.Execute;"。这是我的代码伙计们:

procedure TThreadBoleto.Execute;
var
  RCtx : TRttiContext;
  RType : TRttiType;
  RProp : TRttiProperty;
  I : integer;
  PDF : TFile;
begin
  try
    try
      RClient := TRESTClient.Create('');
      with RClient do begin
        AcceptEncoding := 'identity';
        FallbackCharsetEncoding := 'UTF-8';
        Accept := 'application/json;text/plain;application/pdf;';
        AcceptCharset := 'UTF-8';
        BaseURL := 'https://sandbox.boletocloud.com/api/v1/boletos';
        ContentType := 'application/x-www-form-urlencoded';
        HandleRedirects := true;

        RCtx := TRttiContext.Create;
        RType := RCtx.GetType(THRBoleto.ClassType);
        I := 0;
        for RProp in RType.GetProperties do
        begin
          Params.AddItem;
          Params.Items[I].name := LowerCase(RProp.Name.Replace('_','.'));
          Params.Items[I].Value := RProp.GetValue(THRBoleto).AsString;
          I := I + 1;
        end;
      end;

      RRequest := TRESTRequest.Create(RRequest);
      with RRequest do begin
        Accept := 'application/json;text/plain;application/pdf;';
        Client := RClient;
        Method := rmPost;
        SynchronizedEvents := false;
        AcceptCharset := 'UTF-8';
      end;

      RResponse := TRESTResponse.Create(RResponse);
      RResponse.ContentType := 'application/pdf;*/*;';
      RResponse.ContentEncoding := 'UTF-8';

      RAuth := THTTPBasicAuthenticator.Create('','');
      with RAuth do begin
        Username := 'anAPItokenAccess';
        Password := 'token';
      end;

      RClient.Authenticator := RAuth;
      RRequest.Response := RResponse;

      RRequest.Execute;
      PDF.WriteAllBytes(ExtractFilePath(Application.ExeName)+'boleto.pdf',RResponse.RawBytes);
      OutputStrings.Add(RResponse.Content);
      OutputStrings.Add('');
      OutputStrings.Add('');
      OutputStrings.AddStrings(RResponse.Headers);
    except on E:Exception do
      ShowMessage('Error: '+E.Message);
    end;
  finally
  THRBoleto.Free;
  end;
end;

您确定错误是在 RRequest.Execute() 调用中发生的吗?当您阅读 RResponse.Content 属性 时,您正在尝试将 PDF 数据作为 String 接收,因此我希望该调用会出现 Unicode 错误。 PDF 文件不是文本数据,它是二进制数据,因此接收它的唯一安全方法是使用 RResponse.RawBytes 属性.

此外,您根本不应该设置 RResponse.ContentTypeRResponse.ContentEncoding 属性(更不用说您将它们设置为无效值)。将根据收到的实际回复填写。

由于您将 RRequest.Accept 属性 设置为包含您愿意在响应中接受的 3 种不同媒体类型,因此您需要查看 RResponse.ContentType 属性 值以确保您在将 RawBytes 保存到 .pdf 文件之前确实收到了 PDF 文件。如果您收到的是文本或 JSON 回复,则无法将它们作为 PDF 处理。

就我个人而言,我发现 REST 组件有很多问题。使用 Indy 的 TIdHTTP 可能会更好,例如:

uses
  ..., IdGlobal, IdGlobalProtocols, IdHTTP, IdSSLOpenSSL;

procedure TThreadBoleto.Execute;
var
  RCtx : TRttiContext;
  RType : TRttiType;
  RProp : TRttiProperty;
  Client: TIdHTTP;
  Params: TStringList;
  Response: TMemoryStream;
begin
  Client := TIdHTTP.Create;
  try
    with Client.Request do begin
      AcceptEncoding := 'identity';
      Accept := 'application/json;text/plain;application/pdf';
      AcceptCharset := 'UTF-8';
      ContentType := 'application/x-www-form-urlencoded';
      BasicAuthentication := True;
      Username := 'anAPItokenAccess';
      Password := 'token';
    end;
    Client.HandleRedirects := true;

    RCtx := TRttiContext.Create;
    RType := RCtx.GetType(THRBoleto.ClassType);

    Response := TMemoryStream.Create;
    try
      Params := TStringList.Create;
      try
        for RProp in RType.GetProperties do
          Params.Add(LowerCase(RProp.Name.Replace('_','.')) + '=' + RProp.GetValue(THRBoleto).AsString);

        Client.Post('https://sandbox.boletocloud.com/api/v1/boletos', Params, Response);
      finally
        Params.Free;
      end;

      Response.Position := 0;
      case PosInStrArray(ExtractHeaderMediaType(Client.Response.ContentType), ['application/pdf', 'application/json', 'text/plain'], False) of
        0: begin
          // save PDF
          Response.SaveToFile(ExtractFilePath(Application.ExeName)+'boleto.pdf');
          OutputStrings.Add('[PDF file]');
        end;
        1: begin
          // process JSON as needed
          OutputStrings.Add(ReadStringAsCharset(Response, Client.Response.Charset));
        end;
        2: begin
          // process Text as needed
          OutputStrings.Add(ReadStringAsCharset(Response, Client.Response.Charset));
        end;
      else
        // something else!
        OutputStrings.Add('[Unexpected!]');
      end;
    finally
      Response.Free;
    end;

    OutputStrings.Add('');
    OutputStrings.Add('');
    OutputStrings.AddStrings(Client.Response.RawHeaders);
  finally
    Client.Free;
  end;
end;

procedure TThreadBoleto.DoTerminate;
begin
  if FatalException <> nil then
  begin
    // Note: ShowMessage() is NOT thread-safe!
    ShowMessage('Error: ' + Exception(FatalException).Message);
  end;
  THRBoleto.Free;
  inherited;
end;