Delphi: 如何使用 IdHTTP 调用 Google URL 缩短服务时遇到 400 Bad Request 错误

4
我正在尝试在Delphi中通过API访问URL缩短服务(http://goo.gl/),但是我只收到一个HTTP/1.0 400 Bad Request (reason: parseError)的错误信息。

以下是我的代码(在一个带有Button1Memo1IdHTTP1的表单上,IdHTTP1使用IdSSLIOHandlerSocketOpenSSL1作为其IOHandler。我从http://indy.fulgan.com/SSL/获取了必要的32位OpenSSL DLL,并将它们放在.exe文件所在的目录中):

procedure TFrmMain.Button1Click(Sender: TObject);
    var html, actionurl: String;
    makeshort: TStringList;
begin
try
 makeshort := TStringList.Create;

 actionurl := 'https://www.googleapis.com/urlshortener/v1/url';
 makeshort.Add('{"longUrl": "http://slashdot.org/stories"}');

 IdHttp1.Request.ContentType := 'application/json';
 //IdHTTP1.Request.ContentEncoding := 'UTF-8'; //Using this gives error 415

 html := IdHTTP1.Post(actionurl, makeshort);
 memo1.lines.add(idHTTP1.response.ResponseText);

     except on e: EIdHTTPProtocolException do
        begin
            memo1.lines.add(idHTTP1.response.ResponseText);
            memo1.lines.add(e.ErrorMessage);
        end;
    end;

 memo1.Lines.add(html);
 makeshort.Free;
end;

更新:在此示例中,我留下了我的API密钥(通常可以没有几次尝试也能正常工作),但如果您想要尝试使用您自己的密钥,您可以用'https://www.googleapis.com/urlshortener/v1/url?key=<yourapikey>';替换actionurl字符串。

ParseError消息让我相信在发布longurl时可能存在编码问题,但我不知道该改变什么。

我已经重复检查这个问题很长一段时间了,我相信错误就在我的眼前-我现在只是看不到它。

非常感谢您的帮助!

谢谢!


最终我自己找到了答案,它很好用,而且跟 API 密钥无关。不幸的是,由于我是新用户,出于某种难以理解的原因,我被阻止在这个网站上发布自己的带有新代码的答案,还要再等 8 小时,因为结果放不进评论框里。我不知道那个限制的目的是什么。唉,没办法。 - user1579166
2个回答

4

正如您发现的那样,使用TStrings重载版本的TIdHTTP.Post()方法是错误的。它发送了一个application/x-www-form-urlencoded格式的请求,这对于JSON格式的请求是不合适的。您必须使用TStream重载版本的TIdHTTP.Post()方法,例如:

procedure TFrmMain.Button1Click(Sender: TObject); 
var
  html, actionurl: String; 
  makeshort: TMemoryStream; 
begin 
  try
    makeshort := TMemoryStream.Create; 
    try 
      actionurl := 'https://www.googleapis.com/urlshortener/v1/url'; 
      WriteStringToStream(makeshort, '{"longUrl": "http://slashdot.org/stories"}', IndyUTF8Encoding); 
      makeshort.Position := 0;

      IdHTTP1.Request.ContentType := 'application/json'; 
      IdHTTP1.Request.Charset := 'utf-8';

      html := IdHTTP1.Post(actionurl, makeshort); 
    finally
      makeshort.Free; 
    end;

    Memo1.Lines.Add(IdHTTP1.Response.ResponseText); 
    Memo1.Lines.Add(html); 
  except
    on e: Exception do 
    begin 
      Memo1.Lines.Add(e.Message); 
      if e is EIdHTTPProtocolException then
        Memo1.lines.Add(EIdHTTPProtocolException(e).ErrorMessage); 
    end; 
  end; 
end; 

2

来自短链接器API文档:

每个发送到Google URL Shortener API的请求都需要将您的应用程序标识给Google。有两种方法可以标识您的应用程序:使用OAuth 2.0令牌(也授权请求)和/或使用应用程序的API密钥。

您提供的示例中没有包含OAuth或API密钥身份验证的代码。

要使用API密钥进行身份验证,文档很清楚:

获得API密钥后,您的应用程序可以将查询参数key=yourAPIKey附加到所有请求URL中。


感谢您的快速回复。这里不需要API密钥(尽管正如他们所说,“强烈建议”)。对于少数尝试,通常可以在没有API密钥的情况下进行。此外,我上面提到的错误(“parseError”)并不指向apikey问题,我认为。无论如何,我刚刚通过修改字符串“actionurl”来尝试使用我的密钥: https://www.googleapis.com/urlshortener/v1/url?key=<myapikey>没有任何变化,错误仍然完全相同。 还有其他想法吗? - user1579166
你可以使用curl并使用Fiddler或WireShark捕获请求,然后进行比较。也许使用TStringList参数的Post函数没有做对(基于流的Post在当前Indy版本中可能效果更好)。 - mjn
这个阶段似乎有点过度了,不是吗?我也不确定这是否真的有助于事情的进行,因为语法和方法显然与Delphi非常不同。所以,即使它能够使用curl工作,对我的Pascal代码来说仍然毫无意义... 你看,我认为我的代码中只是一个明显的拼写错误或遗漏,会立刻引起某人的注意。我的意思是,知道你们中是否有人能够使用相同的代码重现解析错误可能会有所帮助。 - user1579166
1
带有TStringList参数的Post函数没有正常工作(基于流的Post可能会更好)。您能给我一个示例吗?我不太确定您的意思。谢谢! - user1579166
最终我自己找到了答案,它运行得很好,而且实际上与API密钥毫无关系。不幸的是,由于我是新用户,出于某种难以理解的原因,我被限制在这个网站上发表包含新代码的答案还需要等待8小时。我不知道这个限制的意图是什么。嗯,太糟糕了。 - user1579166

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接