You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将C++ API POST请求转为Delphi代码时遇400错误求助

Delphi Indy POST请求返回400 Bad Request问题排查

我用Delphi开发,尝试将一段C++的API POST请求代码转换为Delphi代码,采用Indy组件,但调用接口时始终返回400 Bad Request错误。以下是原代码和我写的Delphi代码,求排查问题:

原C++代码

CURL *hnd = curl_easy_init();

curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(hnd, CURLOPT_URL, "https://sandbox.checkbook.io/v3/check/digital");

struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "accept: application/json");
headers = curl_slist_append(headers, "content-type: application/json");
headers = curl_slist_append(headers, "Authorization: xxxxxxxxxxxxxxxx:xxxxxxxxxxxxxxxxx");
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers);

curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, "{\"recipient\":\"testing@checkbook.io\",\"name\":\"Widgets Inc.\",\"amount\":5,\"description\":\"Test Payment\"}");

CURLcode ret = curl_easy_perform(hnd);

我编写的Delphi代码

unit API_InvoiceCloud;

interface

uses
  DB, SysUtils,  System.Classes, System.JSON, IdSSLOpenSSL, VCL.Dialogs,
  IdHTTP, XML.XMLIntf, xml.xmlDom, xml.XMLDoc, IDCoder, IDCoderMIME,
  IdBaseComponent, IdException{, IdZLibCompressorBase{, IdCompressorZLib{,Rest.Client};

procedure CreateDigitalPayment_CheckBookAPI(mRecipientEmailAddress,
                                      mRecipientName : String;
                                      mPaymentAmount : Double;
                                      mPaymentNumber,
                                      mPaymentDescription : String);


implementation

var
  { INDY COMPONENT TO CONNECT TO API SERVER; MAKES CONNECTION }
  IDHTTP1 : TidHttp;
  { SSL Connection }
  SSL : TIdSSLIOHandlerSocketOpenSSL;
  { Request and Response vars }
  JsonRequest, InJson : String;
  JsonToSend : TStringStream;    //object to store json text and pass API
  JObj : TJSONObject;
Const
  { Constant variables holding the APIKEY+APISECRET and BASEURL }
  nBASEURL = 'https://sandbox.checkbook.io/v3/check/digital';
  nAPIKEY = 'xxxxxxxxx:xxxxxxxx';

procedure CreateDigitalPayment_CheckBookAPI(mRecipientEmailAddress,
                                            mRecipientName : String;
                                            mPaymentAmount : Double;
                                            mPaymentNumber,
                                            mPaymentDescription : String);
var
  { Response into String }
  SinglePartyResponse : String;
  ResponseCode : String;  
  { -----------Testing---------- }
  //lParamList: TStringList;
  nBASEURL : String;
  RequestBody : TStream;
  ResponseBody : String;
begin
  
  { JSON body with request string }
  JsonRequest := '{"recipient":"' + mRecipientEmailAddress
                + '","name":"' + mRecipientName
                + '","amount":"' + FloatToStr(mPaymentAmount)
                + '","number":"' + mPaymentNumber
                + '","description":"' + mPaymentDescription + '"}';
  
  try

    try
      { Create connection instance }
      IDHTTP1 := TidHttp.Create;

      { SSL Configuration }
      SSL := TIdSSLIOHandlerSocketOpenSSL.Create;
      SSL.SSLOptions.SSLVersions := [sslvTLSv1_1, sslvTLSv1_2];
      IDHTTP1.IOHandler := SSL;

      { Headers/Params }
      IDHTTP1.Request.Clear;
      IDHTTP1.Request.CustomHeaders.FoldLines := False;
      IDHTTP1.Request.Accept := 'application/json';
      IDHTTP1.Request.ContentType := 'application/json';
      IDHTTP1.Request.CustomHeaders.Values['Authorization'] := nAPIKEY;
      
      { Saving JSON text to TStringStream Object }
      JsonToSend := TStringStream.Create(JsonRequest, TEncoding.UTF8);
      //JsonToSend := TStringStream.Create(JsonRequest, TEncoding.ASCII);

      { Making POST Request using INDYs TidHTTP component; Params are: URL, JsonStringObj - saving into variable }
      SinglePartyResponse := IDHTTP1.Post(nBASEURL, JsonToSend);
      
      ShowMessage(IDHTTP1.ResponseCode.ToString);

    except
      on E : Exception do
        { Display error message if cannot do API CALL }
        begin
          ShowMessage(E.ClassName+' error raised, with message : "' + E.Message + '".');
          Abort;
        end
    end;

  finally
    { Free objects from memory }
    IDHTTP1.Free;
    SSL.Free;
    JsonToSend.Free;
  end;
  
end;

end.

排查关键点

  • JSON格式不匹配:
    1. 原C++代码中amount是数字类型(无引号),但Delphi代码中给amount加了双引号,变成字符串类型,API会因参数类型错误返回400。需修改为:"amount":' + FloatToStr(mPaymentAmount) + ','
    2. 原C++的JSON没有number字段,Delphi代码额外添加了这个字段,API可能不识别该参数,导致请求无效,需删除"number":"' + mPaymentNumber + '",这部分。
  • 手动拼接JSON的风险:
    如果参数中包含双引号、反斜杠等特殊字符,手动拼接会破坏JSON结构。建议用TJSONObject构建JSON,避免语法错误:
    JObj := TJSONObject.Create;
    try
      JObj.AddPair('recipient', mRecipientEmailAddress);
      JObj.AddPair('name', mRecipientName);
      JObj.AddPair('amount', TJSONNumber.Create(mPaymentAmount));
      JObj.AddPair('description', mPaymentDescription);
      JsonRequest := JObj.ToString;
    finally
      JObj.Free;
    end;
    
  • Indy请求优化:
    1. 捕获EIdHTTPProtocolException以获取API返回的具体错误信息(比如"invalid amount"),而不是仅捕获通用Exception:
      except
        on E: EIdHTTPProtocolException do
          ShowMessage('API Error: ' + E.ResponseText);
        on E: Exception do
          ShowMessage('Error: ' + E.Message);
      end;
      
    2. 确保TStringStream的指针在请求前指向开头:JsonToSend.Position := 0;,避免Indy发送空内容。
    3. 设置IDHTTP1.Request.ContentLength := JsonToSend.Size;,让服务器明确知道请求体大小。

内容的提问来源于stack exchange,提问作者robhercarloz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:25:23