http post 数据到底怎么弄?(100分)

哈哈,我用Synapse, 只用三行代码:

fHTTP := THTTPSend.Create;
fHTTP.Document.write(...); //写一个流, 是待POST的数据
fHTTP.HTTPMethod('POST', aURL);
//OK啦, 结果被返回到fHTTP.Document中了.

哈哈.
 
一会去坐火车,回农村安心修养,indy就拜托你了。呵呵
 
OH...看了一下Indy的代码和帮助, 是这样的:
function Post(AURL: string; const ASource: TStrings): string; overload;
function Post(AURL: string; const ASource: TStream): string; overload;
function Post(AURL: string; const ASource: TIdMultiPartFormDataStream): string; overload;

procedure Post(AURL: string; const ASource: TStrings; const AResponseContent: TStream); overload;
procedure Post(AURL: string; const ASource: TStream; const AResponseContent: TStream); overload;
procedure Post(AURL: string; const ASource: TIdMultiPartFormDataStream; AResponseContent: TStream); overload;

1.
---------------------------------------------------------------
前三个方法和后三个方法是成对的, 意思差不多. function post()的返回值与procedure post()中的AResponseContent入口
参数是相当的, 是这样的一种关系:
Result_for_PostFunctionReturn
=
(Param_AResponseContent_for_PostProcedure as TStringStream).DataString


2.
---------------------------------------------------------------
AResponseContent的真实意义是怎样的?

由于一个POST方法向服务器发出数据后, 服务器会返回数据到Client, 无论这个Client
是你的应用, 或者是IE都是一样. IE将这个结果显示出来, 而在Indy实现的应用的Client
端, 这个返回结果被放在AResponseContent中. 当然, 如果你用Function式的调用, 则
结果会被强制处理成一个String返回.

3.
---------------------------------------------------------------
ASource到底是什么?

ASource很奇怪, 它几乎不符合我对HTTP Client的所有思考(我是说为了编程便捷的思考),
直到我读了它的源代码才发现, 它实际上就是POST数据块的Context(上下文), 也就是说,
你在写Client端时, 需要足够的考虑HTTP协议中POST数据块的结构!

GOD!

ASource的数据类型有三种,
ASource: TStrings;
ASource: TStream;
ASource: TIdMultiPartFormDataStream;
其中, TStrings和TStream是完全一样的, 都是将它们的内容直接作为内文POST到Server,
TStrings这种情况时, 相当于将ASource.SaveToStream的结果作为TStream处理.

通常如果POST的数据较小, 我们可以直接使用TStrings或TStream的ASource, 当然, 这种
情况下, 你需要自己来组织数据块(下一段详述).

而如果数据量比较大的话, 你就需要使用TIdMultiPartFormDataStream这个类型的ASource
了. 但使用这个类型的话, 却比较易用, 并不需要更多的理解POST数据块的结构.

----真奇怪Indy为什么要这样处理, 事实上, 如果三种调用方法都按TIdMultiPartFormDataStream
的方式来处理, 编程会容易得多. 我是指这样可以避免程序员去了解Http Client Data_Block Structure!

使用TIdMultiPartFormDataStream时, 只需要Create一个对象实例, 然后调用以下方法:
AddFile()
AddFormField()
AddObject()
添加表单数据即可. 本例中, 千中元的数据就可以如下处理就OK了.
AObj := TIdMultiPartFormDataStream.Create;
AObj.AddFormField('userid', EditUser.text);
AObj.AddFormField('userpwd', EditPass.Text);
AObj.AddFormField('zhuanye', EditSpecification.Text);
if ARadioGroup.ItemIndex = 0 then
AObj.AddFormField('usertype', 'r1') //用户
else
AObj.AddFormField('usertype', 'r2'); //管理员
s := IdHTTP.Post(AURL, AObj);

在TIdMultiPartFormDataStream中, Indy为自动设定Boundary和RequestContentType两个
属性, 你不能修改它们. 这使得你可以完全忽略"MultiPartFormData"这种HTTP Post数据
块的结构.

但是, 我需要提醒的是, 使用这种方法虽然简单, 但是, 却不是高效的. 介于HTTP协议的
规定, 使用MultiPartFormData将会导致更多次的C/S连接和更多的冗余的数据. 因此, 我
仅建议在大量的数据(超过48K或不定长文件)传输时使用这种方法.

4.
---------------------------------------------------------------
如何使用TStrings作为ASource?

由于使用TStream和Strings是几乎同样的, 因此, 我只需要介绍TStrings.

如上所述, 我们需要先了解Post数据块的结构, 如果你用HttpSpy或者HTTPTracer
一类的工具来查看一个HTTP POST请求, 你会发现, 它实际上是(类似)这样的
-------------------------------------------------------------------------
POST http://127.0.0.1/ HTTP/1.0
User-Agent: Mozilla/4.06 [en] (Win95; I)
Host: 127.0.0.1
Accept: image/gif, image/jpeg, image/pjpeg, image/png, */*

userid=abc&userpwd=def&zhuanye=ghi&usertype=r1&B1=%C8%B7%B6%A8
-------------------------------------------------------------------------

前面一部分被称做HTTP Head, 在indy中, 这一部分是不需要关心的, 因为Indy会比
较智能地组织它, indy组织它的依据是TIdHTTP.Request的设定(可以在Delphi IDE中
设置), 通常情况下, 缺省设置是满足要求的.

最后面这一行就是Post的上下文了, 也就是ASource要求的内容, 它必须按照上面所示
的结构, 由你自己来完成它的组织. 明白这一点, 就很容易了. 哈哈.

按照千中元的需求, 我给出基本的代码:
AObj := TStringList.Create;
AObj.Add('userid=' + EditUser.text);
AObj.Add('userpwd=' + EditPass.text);
AObj.Add('zhuanye=' + EditSpecification.text);
if ARadioGroup.ItemIndex = 0 then
AObj.Add('usertype=r1') //用户
else
AObj.Add('usertype=r2'); //管理员
s := IdHTTP.Post(AURL, AObj);

这里需要留意一点, 实际上HTTP Post上下文中要求的表单字段分隔符应该是"&"符号,
而Delphi的TStringList.Text使用的却是"#$0D#$0A"来作为分隔符. indy处理了这个
问题, 在Post()中, 它会调用这样一行:
S := StringReplace(AStrings.Text, sLineBreak, '&', [rfReplaceall])
从而使表单字段分隔符最终满足HTTP协议的规定.

因此, 如果你需要用TStream来完成上述的代码, 在组织Buffer时, 你需要考虑上述
的关于"&"分隔符的问题.

5.
---------------------------------------------------------------
其它.

SeaSky最开始的关于Post()方法的使用的思路是正确的, 用这种形式
st.Add('userid='+ UrlEncode(EditUser.Text) + '&userpwd=' + UrlEncode(EditPass.Text) + '&zhuanye='+UrlEncode(EditSpecification.Text)
+'&usertype=r1' + '&B1=确定')
是可以的, 但是, 没有必要调用UrlEncode(), 因为这里使用的是Post()方法.

按照以上代码编写, Client端铁定是没有错的了. 如果仍然有问题, 你就得看服务器端的CGI
代码了, 或者是你的那个pwd.asp的问题吧, 你可以用一个ISAPI CGI来取值查看一下, 上述的
表单字段应该正确地传上来了. 哈哈.
ISAPI嘛, 随便就可以写一个啦...当然, 用asp也可以, 不过, 我从来不用它. 哈哈.
 
演示代码就不给你写了, 打开了Delphi, 装完Indy, 分析完上述的问题, 最后一想,
真没什么代码可以写的. 哈哈.
 
aimingoo:
有分给你。
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1659037
 
以前写的,自动登陆站点用的
procedure TFrmUrlCommit.CommitUrlData(vRefererUrl: OleVariant;
UserField, UserValue, PasswordField, PasswordValue: string;
OtherPostData: OleVariant);
// 内部小函数,把提交字符串写入variant;
function GetPostData(Content: string): OleVariant;
var
I: Integer;
begin
Result := VarArrayCreate([0, length(content)], varByte);
for i := 0 to Length(content) - 1 do
begin
Result := Ord(content[i + 1]);
end;
Result[Length(content)] := 0;
end;
// 内部小函数结束
var
vHeaders, vPostData, vFrame, vFlags, vCommitData: OleVariant;
aWBCommit: TWebBrowser;
begin
aWBCommit := TWebBrowser.Create(nil);
vHeaders := 'Content-Type: application/x-www-form-urlencoded' + #10#13#0;

if VarIsNull(OtherPostData) then
vCommitData := UserField + '=' + UserValue + '&' +
PasswordField + '=' + PasswordValue
else
vCommitData := UserField + '=' + UserValue + '&' +
PasswordField + '=' + PasswordValue + '&' + OtherPostData;
vPostData := GetPostData(vCommitData);
vFlags := 31;
TVarData(vPostData).vType := varArray;
aWBCommit.Navigate2(vRefererUrl, vFlags, vFrame, vPostData, vHeaders);
FreeAndNil(aWBCommit);
end;
 
顶部