【问题标题】:IdHTTP how to send x-www-form-urlencoded bodyIdHTTP 如何发送 x-www-form-urlencoded 正文
【发布时间】:2019-12-10 11:23:46
【问题描述】:

我已经在 PostMan 中测试了 POST 函数,以使用以下正文参数执行 POST 函数:

这里是 eB​​ay 的这个功能的文档:

  HTTP method:   POST
  URL (Sandbox): https://api.sandbox.ebay.com/identity/v1/oauth2/token

  HTTP headers:
    Content-Type = application/x-www-form-urlencoded
    Authorization = Basic <B64-encoded-oauth-credentials>

  Request body:
    grant_type=authorization_code
    code=<authorization-code-value>
    redirect_uri=<RuName-value>

我的第一次尝试如下:

function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
  xRequestBody: TStringStream;
begin
  with objHTTP.Request do begin
    CustomHeaders.Clear;
    ContentType := 'application/x-www-form-urlencoded';
    CustomHeaders.Values['Authorization'] := 'Basic ' + 'V2VpbmluZ0MtV2VpbmluZ0M';
  end;

  xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + ','
                                     + 'code=' + 'v%5E1.1%23i%5E1%23f%5E0%23r%5E1%23I%5E3%23p%5E3' + ','
                                     + 'redirect_uri=' + 'myredirecturlnameinsandbox',
                                        TEncoding.UTF8);

  try
    try
      Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
    except
      on E: Exception do
        ShowMessage('Error on request: ' + #13#10 + e.Message);
    end;
  finally
    xRequestBody.Free;
  end;
end;

第二次尝试使用下面的 Body 代码

  xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + '&'
                                     + 'code=' + AAuthCode + '&'
                                     + 'redirect_uri=' + gsRuName,
                                        TEncoding.UTF8);

两次尝试都给出 HTTP/1.1 400 Bad Request。

我在 Stack Overflow 中进行了一些搜索,这是最接近的问题。唯一不同的部分是 POST 的正文。

IdHTTP how to send raw body

谁能告诉我分配 POST 正文部分的正确方法是什么? 谢谢。

【问题讨论】:

  • 代码中有几处你做错了。除此之外,您说您收到了400 Bad Request,但这与 Postman 中的相同。那么这里真正的问题是什么?如何通过邮递员邮寄?如何通过 Indy 发布?如何使用易趣 API?无论如何,我将您的示例代码粘贴到 Delphi 10.2 中的普通 VCL 应用程序中,我得到了401 Unathorized。有什么你没有给我们看的吗?
  • @PeterWolf 感谢您的帮助。示例代码中未完全粘贴三个参数(['Authorization']、'code='、'redirect_uri=')。此 POST 调用之后是另一个 GET 调用,以获取 POST 正文中的代码值,该代码在 eBay 授予后 299 秒后过期。使用您的 cmets,现在我发现 Postman 的状态为“400 Bad Request”。让我在 Postman 中进行更多测试。
  • 一个问题:{ "error": "invalid_grant", "error_description": "提供的授权授权码无效或已发给另一个客户端" } 这个消息是显示在 Postman 中,我认为这是来自服务器的反馈。如果是这样,这是否意味着 POST 调用正常但授权码无效?谢谢。
  • 是的,您的 POST 请求已发送到服务器,并以 JSON 内容进行响应。服务器提示为什么请求无法成功服务(401 - 授权问题,400 - 格式错误的请求)。在我的第一条评论中,我写道您的代码中有一些问题。请参阅 Remy 的回答,这几乎总结了它们。
  • @PeterWolf 感谢您的快速回复。我正在用 Remy 的代码做更多的测试。

标签: delphi http-post ebay-api idhttp x-www-form-urlencoded


【解决方案1】:

使用TIdHTTP 发送application/x-www-form-urlencoded 请求的首选方法是使用将TStrings 作为输入的重载TIdHTTP.Post() 方法。您没有以正确的application/x-www-form-urlencoded 格式发送您的TStringStream 数据。

您不需要使用TIdHTTP.Request.CustomHeaders 属性来设置Basic 授权。 TIdHTTP 内置了对Basic 的支持,只需根据需要使用TIdHTTP.Request.UserNameTIdHTTP.Request.Password 属性,并将TIdHTTP.Request.BasicAuthentication 属性设置为true。

试试这个:

function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
  xRequestBody: TStringList;
begin
  with objHTTP.Request do
  begin
    Clear;
    ContentType := 'application/x-www-form-urlencoded';
    BasicAuthentication := True;
    UserName := ...;
    Password := ...;
  end;

  xRequestBody := TStringList.Create;
  try
    xRequestBody.Add('grant_type=' + 'authorization_code');
    xRequestBody.Add('code=' + AAuthCode);
    xRequestBody.Add('redirect_uri=' + 'myredirecturlnameinsandbox');

    try
      Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
    except
      on E: Exception do
        ShowMessage('Error on request: ' + #13#10 + e.Message);
    end;
  finally
    xRequestBody.Free;
  end;
end;

如果你想发送自己的TStringStream,试试这个:

function udxEBayExchangeAuthCodeForUserToken(AAuthCode: String; AIsProduction: Boolean): String;
var
  xRequestBody: TStringStream;
begin
  with objHTTP.Request do
  begin
    Clear;
    ContentType := 'application/x-www-form-urlencoded';
    BasicAuthentication := True;
    UserName := ...;
    Password := ...;
  end;

  xRequestBody := TStringStream.Create('grant_type=' + 'authorization_code' + '&'
                                     + 'code=' + TIdURI.ParamsEncode(AAuthCode){'v%5E1.1%23i%5E1%23f%5E0%23r%5E1%23I%5E3%23p%5E3'} + '&'
                                     + 'redirect_uri=' + 'myredirecturlnameinsandbox',
                                        TEncoding.UTF8);
  try
    try
      xRequestBody.Position := 0;

      Result := objHTTP.Post('https://api.sandbox.ebay.com/identity/v1/oauth2/token', xRequestBody);
    except
      on E: Exception do
        ShowMessage('Error on request: ' + #13#10 + e.Message);
    end;
  finally
    xRequestBody.Free;
  end;
end;

【讨论】:

  • 感谢您的详细解释和示例代码。我确实尝试了两者,但仍然发生相同的错误。我没有改变的一件事是 objHTTP.Request.UserName/Password。只有授权用于 eBay API 调用,我认为用户名已经编码在授权代码中。
  • 我在 Postman 中使用新的 AuthCode 进行了更多测试,使用有效/无效 ['Authorization'],服务器会给出不同的错误:invalid_grant (400 Bad Request)/invalid_client (401 Unauthorized)。在我的应用程序中尝试相同,错误代码与 Postman 相同:400 vs 401。看起来 POST 调用已正确粘贴到服务器,但还有其他不正确的地方。它可能是 POST 调用正文或其他内容中的“code=”。继续使用 Remy 的新代码进行测试并寻求进一步的建议。谢谢。
【解决方案2】:

我被这个问题困扰,但答案并没有太大作用。后来,我用抓包来查看实际发送的内容。给有同样问题的朋友。

function TFormMain.init_web_account(): Boolean;
var
  strTmp: Ansistring;
  PostStringData: TstringStream;
  idhttp1: Tidhttp;
  json, json_array1: TQjson;
  itype_version: integer;
  ifunction_version: integer;
  s: string;
  url: ansistring;
  idMultiPartFormDataStream:TIdMultiPartFormDataStream;
begin
  try
    result := False;
    idhttp1 := TIdHTTP.Create();
    PostStringData := TStringStream.Create;



    //init_string  http://x.x.x.x:8000/user/key?ke1=v1&k2=v2
    strTmp := //
      'strdogsoftid=' + edtDogNumber.Text + //
      '&chipid=' + '' + //
      '&status=' + '0' +  //
      '&reg_user_name=' + edtRegName.Text + //
      '&reg_init_date=' + formatdatetime('yyyy-mm-dd', dtpRegCodeGenDate.Date) + //
      '&lease_expiration=' + formatdatetime('yyyy-mm-dd', dtp_lease_expiration.Date) + //
      '&renew_last_date=' + '' + //
      '&mobile=' + '' + //
      '&weixin=' + '' + //
      '&memo=' + '' + //
      '&function_version=' + IntToStr(rgVersion.ItemIndex) + //
      '&type_version=' + IntToStr(itype_version); //

    url := 'http://x.x.x.x:8000/user/' + edtDogNumber.Text;
    PostStringData.Clear;
    PostStringData.WriteString(strTmp);


    try

      idhttp1.Request.ContentType := 'application/x-www-form-urlencoded';



      s := idhttp1.Post(url, PostStringData); 
      result := True;


    except
      on E: Exception do
      begin
        showmessage(s);
        result := False; 
        exit;
      end;

    end;
  finally
    PostStringData.Free;
    idhttp1.Free;

  end;
end;

【讨论】:

  • 非常感谢你的新代码,dreamnyj。我想知道如何进行数据包捕获以查看发送的实际内容 idHTTP。
  • 首先我们使用postman构造一个标准请求,然后我们使用抓包软件抓取实际发送的内容。捕获软件设置我们的目标地址来实现过滤。具体抓包软件请自行搜索。
【解决方案3】:

如果您使用 TNetHTTPClient,您可以使用“System.Net.Mime”单元通过 HTTP Post 管理多部分表单数据并管理请求。

我使用的是 Delphi 10.4.2

【讨论】:

    猜你喜欢
    • 2017-03-27
    • 2021-03-14
    • 2021-01-19
    • 2020-08-12
    • 2019-05-04
    • 1970-01-01
    • 1970-01-01
    • 2015-02-07
    • 1970-01-01
    相关资源
    最近更新 更多