如何使用WinInet在Delphi 2010中发送HTTP Post请求

4

我希望使用WinInet在Delphi 2010中发送HTTP Post请求,但我的脚本无法正常工作;/

以下是我的Delphi脚本:

uses WinInet;
procedure TForm1.Button1Click(Sender: TObject);
var
  hNet,hURL,hRequest: HINTERNET;
begin
  hNet := InternetOpen(PChar('User Agent'),INTERNET_OPEN_TYPE_PRECONFIG or INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  if Assigned(hNet) then
  begin
  try
    hURL := InternetConnect(hNet,PChar('http://localhost/delphitest.php'),INTERNET_DEFAULT_HTTP_PORT,nil,nil,INTERNET_SERVICE_HTTP,0,DWORD(0));
    if(hURL<>nil) then
      hRequest := HttpOpenRequest(hURL, 'POST', PChar('test=test'),'HTTP/1.0',PChar(''), nil, INTERNET_FLAG_RELOAD or INTERNET_FLAG_PRAGMA_NOCACHE,0);
    if(hRequest<>nil) then
      HttpSendRequest(hRequest, nil, 0, nil, 0);
    InternetCloseHandle(hNet);
  except
      ShowMessage('error');
    end
  end;
end;

以及我的PHP脚本:

$data = $_POST['test'];
$file = "test.txt";
$fp = fopen($file, "a");
flock($fp, 2);
fwrite($fp, $data);
flock($fp, 3);
fclose($fp);

请使用四个空格缩进所有代码行,这样语法高亮才能生效。 - Andreas Rejbrand
1
有时自己做会更容易。您可以编辑问题。 - Uwe Raabe
1
哦,我的错误,但是即使没有它,它仍然无法工作。 - noxwow
3个回答

16

主要问题:

  • InternetConnect 函数的第二个参数应该只包含服务器的名称,而不是服务器端脚本的完整 URL。

  • HttpOpenRequest 函数的第三个参数应该是脚本的文件名(URL),而不是 POST 数据!

  • 实际的 POST 数据应该作为 HttpSendRequest 函数的第四个参数。

次要问题:

  • INTERNET_OPEN_TYPE_PRECONFIG 或 INTERNET_OPEN_TYPE_PRECONFIG:只使用 INTERNET_OPEN_TYPE_PRECONFIG 即可。

  • DWORD(0) 过度了。只使用 0 即可。

示例代码:

我使用以下代码来 POST 数据:

procedure WebPostData(const UserAgent: string; const Server: string; const Resource: string; const Data: AnsiString); overload;
var
  hInet: HINTERNET;
  hHTTP: HINTERNET;
  hReq: HINTERNET;
const
  accept: packed array[0..1] of LPWSTR = (PChar('*/*'), nil);
  header: string = 'Content-Type: application/x-www-form-urlencoded';
begin
  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    hHTTP := InternetConnect(hInet, PChar(Server), INTERNET_DEFAULT_HTTP_PORT, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);
    try
      hReq := HttpOpenRequest(hHTTP, PChar('POST'), PChar(Resource), nil, nil, @accept, 0, 1);
      try
        if not HttpSendRequest(hReq, PChar(header), length(header), PChar(Data), length(Data)) then
          raise Exception.Create('HttpOpenRequest failed. ' + SysErrorMessage(GetLastError));
      finally
        InternetCloseHandle(hReq);
      end;
    finally
      InternetCloseHandle(hHTTP);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

例如:

WebPostData('My UserAgent', 'www.rejbrand.se', 'mydir/myscript.asp', 'value=5');

针对回答者的回应更新

要从互联网读取数据,使用InternetReadFile函数。我使用以下代码从互联网读取一个小的(一行)文本文件:

function WebGetData(const UserAgent: string; const Server: string; const Resource: string): string;
var
  hInet: HINTERNET;
  hURL: HINTERNET;
  Buffer: array[0..1023] of AnsiChar;
  i, BufferLen: cardinal;
begin
  result := '';
  hInet := InternetOpen(PChar(UserAgent), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
  try
    hURL := InternetOpenUrl(hInet, PChar('http://' + Server + Resource), nil, 0, 0, 0);
    try
      repeat
        InternetReadFile(hURL, @Buffer, SizeOf(Buffer), BufferLen);
        if BufferLen = SizeOf(Buffer) then
          result := result + AnsiString(Buffer)
        else if BufferLen > 0 then
          for i := 0 to BufferLen - 1 do
            result := result + Buffer[i];
      until BufferLen = 0;
    finally
      InternetCloseHandle(hURL);
    end;
  finally
    InternetCloseHandle(hInet);
  end;
end;

使用示例:

WebGetData('My UserAgent', 'www.rejbrand.se', '/MyDir/update/ver.txt')

这个函数只是读取数据,没有预先的POST操作。不过,InternetReadFile函数也可以与由HttpOpenRequest创建的句柄一起使用,因此在你的情况下也会起作用。你知道 the WinInet 参考是 MSDN 吗?在那里可以详细描述所有 Windows API 函数,例如InternetReadFile


1
谢谢,我已经合并了这些脚本,现在的脚本看起来像这样: http://paste.org/pastebin/view/19370按预期工作。再次感谢。 - noxwow
在WebPostData中,为什么您使用AnsiString而不是String作为参数Data的类型?String是WideString。 - alancc
另外,为什么您在缓冲区中使用 AnsiChar?这是否意味着响应文本将是 ANSI 而不是 Unicode? - alancc
@alancc:在 Delphi 2009 之前,stringAnsiString;自从 Delphi 2009 以来,string 就变成了 UnicodeStringWideString 是另一种类型。但更重要的是:这段代码只从互联网中读取字节。它不知道字符编码。将字符串更改为 string(=UnicodeString)几乎肯定会导致代码失败,因为互联网文件可能是 ASCII 或 UTF-8(因此英文字母宽度为1个字节),而 string 每个字符占两个字节,所以输出将是垃圾(大多数是随机的中文字符)。使用 AnsiString,这将正确地处理 WWW ASCII 文件。 - Andreas Rejbrand
@AndreasRejbrand,非常感谢。我根据您的方法在Delphi XE3中创建了一个函数。现在,我已确保发送和接收的所有数据都是AnsiString或AnsiChar类型。 - alancc
显示剩余4条评论

2
var
  BufferIn : INTERNET_BUFFERS;
  Buffer: array[0..1024] of Byte;
  FTmp: TSomeStream:
  FURL: string;
  ...
begin
... // Create FTmp, set FUrl, ...

  NetHandle := InternetOpen( 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3',
                          INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);

  UrlHandle := HttpOpenRequest(NetHandle, 'POST', PChar(FURL), nil, nil, nil, INTERNET_FLAG_NO_CACHE_WRITE, 0);

  ... // Check handles, etc
  try
    BufferIn.dwBufferTotal := FTmp.Size;

    if not HttpSendRequestEx(UrlHandle, @BufferIn, nil, 0, 0) then
      raise Exception.CreateFmt('Error on HttpSendRequestEx %d\n', [GetLastError()]);

    size := FTmp.Read(Buffer, 1024);
    InternetWriteFile(UrlHandle, @Buffer, size, BytesWritten);

    while (BytesWritten = size) and (BytesWritten > 0) do
    begin
      size := FTmp.Read(Buffer, 1024);
      InternetWriteFile(UrlHandle, @Buffer, size, BytesWritten);
    end;
  finally
    FTmp.Free;

    InternetCloseHandle(UrlHandle);
    InternetCloseHandle(NetHandle);
  end;

你使用Firefox UserAgent的原因是什么? - Simon Hartcher
1
不,这只是我曾经尝试下载HTML页面时遇到的问题的剩余部分。当时我收到了一条消息,要求我“使用真正的浏览器”,而不是我想要的HTML。 - dmajkic

1

http://www.utilmind.com网站上有一个叫做“Delphi 32的HTTPGet组件”的库。它会将一个可视控件安装到您的组件面板中。 它正好可以完成你所需求的功能,建议您可以看一下。


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