我有一个像这样简单的 Asp .net 页面 http://issamsoft.com/app2/page1.aspx 我想向其发布一些数据并从响应中提取数据, 通过使用 TIdHttp。我尝试在 Delphi2009 中这样做:
Procedure TForm1.Button1Click(Sender: TObject);
Const
VIEWSTATE = '/wEPDwUKMjA3NjE4MDczNmRkSxPt/LdmgqMd+hN+hkbiqIZuGUk=';
EVENTVALIDATION = '/wEWAwL40NXEDALs0bLrBgKM54rGBtmtdOYy+U7IFq8B25bYT1d4o1iK';
FORMPARAMS = 'TextBox1=Issam&Button1=Button';
URL = 'http://issamsoft.com/app2/page1.aspx';
var
http: TIdHttp;
lstParams: TStringList;
begin
http := TIdHTTP.Create(self);
lstParams := TStringList.Create;
try
lstParams.Add('__VIEWSTATE='+VIEWSTATE);
lstParams.Add('__EVENTVALIDATION='+EVENTVALIDATION);
lstParams.Add(FORMPARAMS);
http.Request.ContentType := 'application/x-www-form-urlencoded';
Memo1.Lines.Text := http.Post(url,lstParams);
finally
http.Free;
lstParams.Free;
end;
end;
但是 TIdhttp 总是给出错误(HTTP/1.1 500 内部服务器错误。) 我在 idHttp 单元中读到了一些关于 http 协议(protocol) v 1.1 问题的评论,如下所示:
Currently when issuing a POST, IdHTTP will automatically set the protocol to version 1.0 independently of the value it had initially, This is because there are some servers that don't respect the RFC to the full extent. In particular, they don't respect sending/not sending the Expect: 100-Continue header. Until we find an optimum solution that does NOT break the RFC, we will restrict POSTS to version 1.0.
我的代码有问题还是 TidHttp Bug?如果问题出在 TIdHttp,有什么解决方法吗?或者还有其他使用 Indy 组件的解决方案吗?
此外。我使用 WebClient 在 C# 中制定了一个解决方案,效果非常好。
private void button1_Click(object sender, EventArgs e)
{
WebClient myClient = new WebClient();
string viewstate = HttpUtility.UrlEncodeUnicode(@"/wEPDwUKMjA3NjE4MDczNmRkSxPt/LdmgqMd+hN+hkbiqIZuGUk=");
string eventvaildation = HttpUtility.UrlEncodeUnicode(@"/wEWAwL40NXEDALs0bLrBgKM54rGBtmtdOYy+U7IFq8B25bYT1d4o1iK");
string postdata = "__VIEWSTATE=" + viewstate + "&" +
"__EVENTVALIDATION=" + eventvaildation + "&TextBox1=Issam&Button1=Button";
myClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
byte[] responce = myClient.UploadData("http://issamsoft.com/app2/page1.aspx", Encoding.ASCII.GetBytes(postdata));
txtResponse.Text = Encoding.ASCII.GetString(responce);
}
在 Delphi 中我在哪里可以找到像 WebClient 这样的(好的/可信的)类?免费首选:)
编辑: 我希望 VIEWSTATE,EVENTVALIDATION 的机制对你来说足够清楚,它们是服务器生成的哈希值,它们可能会改变(已经改变),我原来的项目有一段代码只是为了提取当前的 VIEWSTATE,EVENTVALIDATION 值,但我省略该部分只是为了使我的示例简单明了,因此当您想尝试上述代码时,您必须从当前页面源获取 VIEWSTATE,EVENTVALIDATION 值。
最佳答案
这很可能是由于在 C# 中,您使用与号 (&) 字符来连接 VIEWSTATE、EVENTVALIDATION 和 FORMPARAMS 造成的。
但在 Delphi 中,您使用 TStringList 并添加。 Add 不会在加法之间添加“与”符号 (&),而是添加 CR+LF。
因此,您在 Delphi 中发送的数据与 C# 中发送的数据不同。
如果您更改 Delphi 中的字符串连接逻辑以匹配 C# 中的逻辑,则无论您在 Delphi 端使用哪种 WebClient,它都应该有效。
--杰罗恩
编辑:20090830
这段代码有效;使用 Fiddler2 检查 C# 和 IE7 输出,我发现您还需要转义“/”和“=”字符。
编辑2:20090831
不知怎的,自昨天以来,网站上的 VIEWSTATE 和 EVENTVALIDATION 发生了变化(尽管我不确定为什么)。我已经更改了代码,并且在发布我对此答案的更改时它可以工作。 这是从 Internet Explorer 7 中发布页面时每个 Fiddler 捕获的新请求内容:
__VIEWSTATE=%2FwEPDwUKMjA3NjE4MDczNmRk5%2FC2iWwvlAB3L1wYzRpm3KZhRC0%3D&__EVENTVALIDATION=%2FwEWAwLXzuATAuzRsusGAoznisYGSYOqDGy4vMunY6A8xi6ahQEPI5Q%3D&TextBox1=Issam&Button1=Button
这是从 Delphi 示例发布时的新请求:
__VIEWSTATE=%2FwEPDwUKMjA3NjE4MDczNmRk5%2FC2iWwvlAB3L1wYzRpm3KZhRC0%3D&__EVENTVALIDATION=%2FwEWAwLXzuATAuzRsusGAoznisYGSYOqDGy4vMunY6A8xi6ahQEPI5Q%3D&TextBox1=Issam&Button1=Button
下面的示例现在给出了这个结果(注意结果中的“Issam”):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
</title></head>
<body>
<form name="form1" method="post" action="page1.aspx" id="form1">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA3NjE4MDczNg9kFgICAw9kFgICBQ8PFgIeBFRleHQFDVdlbGNvbWUgSXNzYW1kZGSCDMOkTMjkZJgqLkhpK99twpD5+A==" />
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAwKr+O/BBQLs0bLrBgKM54rGBlueO5BU/6BAJMZfHNwh5fsQFuAm" />
<div>
<input name="TextBox1" type="text" value="Issam" id="TextBox1" />
<input type="submit" name="Button1" value="Button" id="Button1" />
<br />
<span id="Label1">Welcome Issam</span>
<br />
</div>
</form>
</body>
</html>
编辑3:20090831
事实上,VIEWSTATE 和 EVENTVALIDATION 再次发生了变化:不知何故,它们不断变化:似乎涉及时间因素。
所以我修改了代码,现在它可以从 GET 请求中提取 VIEWSTATE 和 EVENTVALIDATION,然后将其放入 POST 请求中。
此外,看来'+'和':'也需要转义。 所以我深入研究了ASP.NET页面生成逻辑,发现他们使用HttpUtility.UrlEncode进行转义,最终检查HttpUtility.IsSafe要转义的字符。 所以我改编了Reflector将 Delphi 代码逆向工程到 THttpUtility 类中。
这是最终的代码:
{$DEFINE FIDDLER}
unit HttpPostExampleFormUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP;
type
THttpPostExampleForm = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
procedure Log(const What: string; const Content: string);
procedure LogClear;
end;
var
HttpPostExampleForm: THttpPostExampleForm;
implementation
uses
HttpUtilityUnit;
{$R *.dfm}
procedure AddFormParameter(const StringStream: TStringStream; const ParameterName: string; const ParameterValue: string);
var
EncodedParameterValue: string;
begin
StringStream.WriteString(ParameterName);
StringStream.WriteString('=');
EncodedParameterValue := THttpUtility.UrlEncode(ParameterValue);
StringStream.WriteString(EncodedParameterValue);
end;
procedure AddFormParameterSeparator(const StringStream: TStringStream);
begin
StringStream.WriteString('&');
end;
function ExtractHiddenParameter(const ParameterName: string; const Request: string): string;
//<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA3NjE4MDczNg9kFgICAw9kFgICBQ8PFgIeBFRleHQFDVdlbGNvbWUgSXNzYW1kZGSCDMOkTMjkZJgqLkhpK99twpD5+A==" />
const
PrefixMask = 'input type="hidden" name="%s" id="%s" value="';
Suffix = '" />';
var
Prefix: string;
PrefixLength: Integer;
PrefixPosition: Integer;
SuffixPosition: Integer;
begin
Prefix := Format(PrefixMask, [ParameterName, ParameterName]);
PrefixPosition := Pos(Prefix, Request);
if PrefixPosition = 0 then
Result := ''
else
begin
PrefixLength := Length(Prefix);
Result := Copy(Request,
PrefixPosition + PrefixLength,
1 + Length(Request) - PrefixPosition - PrefixLength);
SuffixPosition := Pos(Suffix, Result);
if SuffixPosition = 0 then
Result := ''
else
Delete(Result, SuffixPosition, 1 + Length(Result) - SuffixPosition);
end;
end;
procedure THttpPostExampleForm.Button1Click(Sender: TObject);
const
DefaultVIEWSTATE = '/wEPDwUKMjA3NjE4MDczNmRk5%2FC2iWwvlAB3L1wYzRpm3KZhRC0=';
DefaultEVENTVALIDATION = '/wEWAwLXzuATAuzRsusGAoznisYGSYOqDGy4vMunY6A8xi6ahQEPI5Q=';
FORMPARAMS = 'TextBox1=Issam&Button1=Button';
URL = 'http://issamsoft.com/app2/page1.aspx';
__VIEWSATE = '__VIEWSTATE';
__EVENTVALIDATION = '__EVENTVALIDATION';
var
VIEWSTATE: string;
EVENTVALIDATION: string;
getHttp: TIdHttp;
getRequest: string;
postHttp: TIdHttp;
ParametersStringStream: TStringStream;
postRequest: string;
begin
LogClear();
getHttp := TIdHTTP.Create(self);
try
getRequest := getHttp.Get(URL);
Log('GET Request', getRequest);
VIEWSTATE := ExtractHiddenParameter(__VIEWSATE, getRequest);
EVENTVALIDATION := ExtractHiddenParameter(__EVENTVALIDATION, getRequest);
Log('Extracted VIEWSTATE', VIEWSTATE);
Log('Extracted EVENTVALIDATION', EVENTVALIDATION);
finally
getHttp.Free();
end;
postHttp := TIdHTTP.Create(self);
{$IFDEF FIDDLER}
postHttp.ProxyParams.ProxyServer := '127.0.0.1';
postHttp.ProxyParams.ProxyPort := 8888;
{$ENDIF FIDDLER}
postHttp.HTTPOptions := postHttp.HTTPOptions + [hoKeepOrigProtocol];
ParametersStringStream := TStringStream.Create('');
try
AddFormParameter(ParametersStringStream, __VIEWSATE, VIEWSTATE);
AddFormParameterSeparator(ParametersStringStream);
AddFormParameter(ParametersStringStream, __EVENTVALIDATION, EVENTVALIDATION);
AddFormParameterSeparator(ParametersStringStream);
ParametersStringStream.WriteString(FORMPARAMS);
postHttp.Request.ContentType := 'application/x-www-form-urlencoded';
ParametersStringStream.Seek(0, soFromBeginning);
Log('POST Parameters', ParametersStringStream.DataString);
postRequest := postHttp.Post(url, ParametersStringStream);
Log('POST Request', postRequest);
finally
postHttp.Free;
ParametersStringStream.Free;
end;
end;
procedure THttpPostExampleForm.Log(const What, Content: string);
begin
Memo1.Lines.Add(What + '=');
Memo1.Lines.Add(Content);
end;
procedure THttpPostExampleForm.LogClear;
begin
Memo1.Lines.Clear;
end;
end.
还有 THttpUtility 类:
unit HttpUtilityUnit;
interface
type
THttpUtility = class
private
class function IsSafe(const ch: Char): boolean;
public
class function UrlEncode(s: string): string;
end;
implementation
uses
Classes, SysUtils;
class function THttpUtility.IsSafe(const ch: Char): boolean;
begin
if ((((ch >= 'a') and (ch <= 'z')) or ((ch >= 'A') and (ch <= 'Z'))) or ((ch >= '0') and (ch <= '9'))) then
Result := True
else
case ch of
'''',
'(',
')',
'*',
'-',
'.',
'_',
'!':
Result := True;
else
Result := False;
end;
end;
class function THttpUtility.UrlEncode(s: string): string;
var
ch: Char;
HexCh: string;
StringStream: TStringStream;
Index: Integer;
Ordinal: Integer;
begin
StringStream := TStringStream.Create('');
try
//Note: this is not yet UTF-16 compatible; check before porting to Delphi 2009
for Index := 1 to Length(s) do
begin
ch := s[Index];
if IsSafe(ch) then
StringStream.WriteString(Ch)
else
begin
Ordinal := Ord(Ch);
HexCh := IntToHex(Ordinal, 2);
StringStream.WriteString('%'+HexCh);
end;
end;
Result := StringStream.DataString;
finally
StringStream.Free;
end;
end;
end.
这应该能让你继续前进。
--杰罗恩
关于asp.net - 使用 Delphi 和 TIdHttp 将数据发布到 ASP .NET 页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1351181/