indy的TIdHTTP控件的使用

hardtoreg 2003-03-31 12:35:54
TIdHTTP控件中的post方法如何使用啊?
我想用post方法把参数传给一个web登陆页,从而得到登陆后页面的html,可却无法办到。
具体代码:
void __fastcall TForm1::postClick(TObject *Sender)
{
TStrings *Source=new TStringList();
TStream *stream=new TStringStream("");
Source->Add(Edit2->Text);
IdHTTP1->Post(Edit1->Text,Source,stream);
Memo1->Text=((TStringStream *)stream)->ReadString(1024*1024);
}
用sniffer观察发现甚至连http交互都没有。郁闷
...全文
1065 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
hardtoreg 2003-04-06
  • 打赏
  • 举报
回复
还是结了吧。非常感谢各位。特别是Lo(水滴)和ronshen(大音)
Lo 2003-04-06
  • 打赏
  • 举报
回复
你的例子我看了,估计是和你登录的网站有关系,你里面有串数字是从哪来的,我估计那数字是每次登录是都会变化的,有点类似PHPSession这样的东西。
你换个网站登录试试看,找个简单点的。
hardtoreg 2003-04-06
  • 打赏
  • 举报
回复
不会是indy90没有安装好吧?
hardtoreg 2003-04-06
  • 打赏
  • 举报
回复
还有,我用post方法提交的过程用sniffer抓下,看不到http交互。仔细一看才发现。它们都包含在tcp包中,但并未作为http交互显示出来。这是什么原因?



Lo 2003-04-04
  • 打赏
  • 举报
回复
你不会是把IdHTTP的ProtocolVersion设成pv1_0了吧,IdHTTP绝对是支持Http 1.1的,连1.1都不支持的话,那组件还有什么用,现在网络上用的Http协议基本都是1.1版本的。

你还是把你的全部代码贴出来吧,省得老出问题,或者你把代码发到我的邮箱里,我帮你调试。

net2m@21cn.com
Kylix_XP 2003-04-04
  • 打赏
  • 举报
回复
<<TCP-IP详解卷1:协议>> 下载:
http://jiurl.cosoft.org.cn/download/TCPIP_Illustracted_1.zip
Lo 2003-04-03
  • 打赏
  • 举报
回复
这个错误是很正常的,意思是说“从容关闭”出错,不过没什么影响,而且它只是在你调试的时候才会弹出来错误,你运行的时候就不会了。

你可以把
IdHTTP下的HTTPOptions下的三项全部设计成true,应该就不会有这错误了。
hardtoreg 2003-04-03
  • 打赏
  • 举报
回复
那么下面这个错误是不是idHTTP的post方法不支持http1.1的缘故啊?
project XXXXXX.exe raised exception class EIdHTTPProtocolExeption with message 'HTTP/1.1 302 Moved Tempoorarily'.Process stopped.Use Step or Run to continue.
ronshen 2003-04-03
  • 打赏
  • 举报
回复
另,
提交Form中的数据,确实是用的POST方法,浏览器的实现也是用POST方法,然后用和我类似的方法来填POST消息体。
当然,在我们访问很多网站的时候,URL栏上都会出现一些参数,就比如这个:
http://expert.csdn.net/Expert/topic/1596/1596850.xml?temp=.796673
象这个的参数不是放在消息体,而是放在URL里边的。如果后台用JAVA处理,URL上带的参数可以从REQUEST消息中获取到。
但一般接收浏览器上Form数据的后台程序,是自己分析POST消息体,然后解析出参数来的。如果你给的不是标准格式,别人当然不会正确处理哪。
ronshen 2003-04-03
  • 打赏
  • 举报
回复
如果是在URL中直接写参数,这个格式很简单,URL中跟"&..."就可以,就象Lo(水滴)给的格式一样。这种其实没必要用POST命令,用个GET就可以了。
但如果是用POST命令的话,它后边跟的是一个HTTP消息体,这个消息体需要自己来填,就必须按照一定的格式来了。

不是用TCP提交数据的问题,而是POST命令的问题(你有SNIFFER,可以做个HTML FORM,用浏览器来提交,然后截获下来看一下噻!),谁让你是用C来做这种事呢?肯定要麻烦一些呐,如果是直接写一个HTML FORM,那多简单啊!:=)
hardtoreg 2003-04-03
  • 打赏
  • 举报
回复
非常感谢ronshen(大音)!
我只是想将一个form中填好的信息提交上去。真的很感谢你!
有一事不明白,idHTTP控件应该已经对http协议进行了封装,应用层以下都应是透明的。可为什么
以你的方式提交数据,对数据的格式有着如此严格的要求?真的象Lo(水滴)所说是通过TCP来提交数据?我真的有些糊涂。
ronshen 2003-04-03
  • 打赏
  • 举报
回复
代码如下:

头文件httpdatastream.h-------->
定义了一个tHttpDataStream类,继承的tmemorystream,主要是两个函数,一个addfield,在HTTP消息体中加一个字段;一个addfile,在HTTP消息体中加一个附加文件;
//---------------------------------------------------------------------------

#ifndef HttpDataStreamH
#define HttpDataStreamH

const AnsiString CONTENT_TYPE="multipart/form-data; boundary=";
const AnsiString CRLF = "\r\n";
const AnsiString CONTENT_DISPOSITION = "Content-Disposition: form-data; name=\"%s\"";
const AnsiString FILENAME_PLACE_HOLDER = "; filename=\"%s\"";
const AnsiString CONTENTTYPE_PLACE_HOLDER = "Content-Type: %s" + CRLF + CRLF;
const AnsiString CONTENT_LENGTH = "Content-Length: %s" + CRLF;

class THttpDataStream : public TMemoryStream
{
public:
__fastcall THttpDataStream();
private:
AnsiString m_Boundary;
AnsiString m_RequestContentType;
AnsiString GenerateUniqueBoundary();
void AddFile(const AnsiString FieldName,const AnsiString FileName,const AnsiString ContentType,TFileStream* FileData);

public:
void AddField(const AnsiString FieldName,const AnsiString FieldValue);
void AddFile(const AnsiString FieldName,const AnsiString FileName,const AnsiString ContentType);
void PrepareStreamForDispatch();

AnsiString GetBoundary();
AnsiString GetRequestContentType();
};
//---------------------------------------------------------------------------
#endif


//*httpdatastream.cpp-------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "HttpDataStream.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)
__fastcall THttpDataStream::THttpDataStream()
: TMemoryStream()
{
m_Boundary = GenerateUniqueBoundary();
m_RequestContentType = CONTENT_TYPE + m_Boundary;
}

//产生HTTP消息中唯一的分隔字符串
AnsiString THttpDataStream::GenerateUniqueBoundary()
{
AnsiString result = "--------------ronshen" + FormatDateTime("yyyymmddhhnnsszzz", Now());
return result;
}

//在HTTP消息中增加一个变量
void THttpDataStream::AddField(const AnsiString FieldName,const AnsiString FieldValue)
{
AnsiString sFormFieldInfo;
AnsiString strTemp = m_Boundary + CRLF + CONTENT_DISPOSITION + CRLF + CRLF +
FieldValue + CRLF;

TVarRec v[] = { FieldName };
sFormFieldInfo = Format(strTemp, v , ARRAYSIZE(v) - 1);
Write((const void *) sFormFieldInfo.c_str(), sFormFieldInfo.Length());
}

//在HTTP消息中增加一个文件
void THttpDataStream::AddFile(const AnsiString FieldName, const AnsiString FileName,
const AnsiString ContentType, TFileStream* FileData)
{
AnsiString sFormFieldInfo;
__int64 iSize;

iSize = FileData->Size;
AnsiString strSize = AnsiString(iSize);
sFormFieldInfo = Format(m_Boundary + CRLF + CONTENT_DISPOSITION +
FILENAME_PLACE_HOLDER + CRLF + CONTENT_LENGTH +
CONTENTTYPE_PLACE_HOLDER, OPENARRAY(TVarRec,(FieldName, FileName, strSize, ContentType)));

Write((const void *) sFormFieldInfo.c_str(), sFormFieldInfo.Length());

FileData->Position = 0;

try{
CopyFrom(FileData,0);
}
catch(...)
{
}

Write((const void*) CRLF.c_str(),CRLF.Length());
}

//在HTTP消息中增加一个文件
void THttpDataStream::AddFile(const AnsiString FieldName, const AnsiString FileName,
const AnsiString ContentType)
{
TFileStream* FileStream;
FileStream = new TFileStream(FileName, fmOpenRead | fmShareDenyWrite);
try {
AddFile(FieldName, FileName, ContentType, FileStream);
}
catch(...)
{
}
FileStream->Free();
}

//在发送消息之前,加一个结束字符串,并将指针位置归0
void THttpDataStream::PrepareStreamForDispatch()
{
AnsiString strEnd;
strEnd = CRLF + m_Boundary + "--" + CRLF;
Write((const void *) strEnd.c_str(), strEnd.Length());
Position = 0;
}

AnsiString THttpDataStream::GetBoundary()
{
return m_Boundary;
}

AnsiString THttpDataStream::GetRequestContentType()
{
return m_RequestContentType;
}

在调用这个类时,比如在一个有两个显示用的memo的form中吧,这样写:

AnsiString strFieldName = "fileno";
TMemoryStream* responseStream = new TMemoryStream();
THttpDataStream* httpStream = new THttpDataStream();

IdHTTP1->Request->ContentType = httpStream->GetRequestContentType();

httpStream->AddField("action","1");
httpStream->AddField("docunid","046080F7D74BFF1348256CE9002DA29A");


httpStream->PrepareStreamForDispatch();
httpStream->Position = 0;
/////////////////////////////////////////////////////////
//显示POST消息流中的所有信息
Memo1->Lines->Clear();
char buffer[1024];
int readChars=1024;
AnsiString strDisp;
while(readChars >= 1024){
readChars = httpStream->Read(buffer,1024);
strDisp = AnsiString(buffer);
Memo1->Lines->Add(strDisp);
}

httpStream->Position = 0;
IdHTTP1->Post(Edit1->Text,httpStream,responseStream);

/////////////////////////////////////////////////////////
//显示回来的消息流中的所有信息
Memo2->Lines->Clear();
responseStream->Position = 0;
readChars = 1024;
while(readChars >= 1024){
readChars = responseStream->Read(buffer,1024);
strDisp = AnsiString(buffer);
Memo2->Lines->Add(strDisp);
}

delete httpStream;
delete responseStream;

这个是目前在实际使用的东东,应该是没有问题的。

当然,如果你只需要提交几个字段的话,或许不需要这么复杂,因为URL里边就可以带了,直接发个GET命令过去都应该可以。POST主要还是适用于需要大量数据传输的场合,比如需要提交一个文件等等。
Lo 2003-04-02
  • 打赏
  • 举报
回复
to hardtoreg(柱子) :
你的Edit2->Text里的内容到底是什么呀?每一个URL要提交的参数都要在Source里Add()一次呀,你是不是把所以的参数都放到Edit2->Text里了?这样当然不行,我给出的例子你还没看明白吗?每个参数都要单独Add()到Source的列表里的。
你还是把你Edit2->Text和Edit1->Text里的内容都完整的,贴出来。

这个方法我现在的软件里一直用着,怎么可能没试过!绝对可行,只是你操作上还有错误。

hardtoreg 2003-04-02
  • 打赏
  • 举报
回复
严格按照水滴的方法,还不行!怎么回事?见鬼了。

真是郁闷死了。很感谢大家的帮助。我都不好意思麻烦大家了。唉!!!!!!
rocsoar 2003-04-02
  • 打赏
  • 举报
回复
这个解决方法你不妨试试看行不行:
AnsiString LOGIN_PAGE="",LOGIN_USER="",LOGIN_PASS="",LOGIN_TYPE="",LOGIN_TYPEVALUE="";
/*
*LOGIN_PAGE是指登录提交的url
*LOGIN_USER是指登录时user的字符串名
*LOGIN_USERVALUE是指登录时user的值
*LOGIN_PASS是指登录时密码的字符串名
*LOGIN_PASSVALUE是指登录时密码的值
*LOGIN_TYPE是指密码提交的方式的字符串名
*LOGIN_TYPEVALUE是指密码提交的方式的值
*如:登录5460。从首页的HTML分析得知,LOGIN_PAGE是:http://www.5460.net/txl/login/login.pl,LOGIN_USER的值是:username,LOGIN_USERVALUE的值是登录时输入的用户名,LOGIN_PASS的值是:passwd,LOGIN_PASSVALUE的值是登录时需要僌的密码,LOGIN_TYPE的值是:ok,LOGIN_TYPEVALUE的值是:确定
*/
memo1->Clear();
memo1->Lines->Add(LOGIN_USER + "=" + LOGIN_USERVALUE);
memo1->Lines->Add(LOGIN_PASS + "=" + LOGIN_PASSVALUE);
memo1->Lines->Add(LOGIN_TYPE + "=" + LOGIN_TYPEVALUE);
try{
TStringStream * returnStr = new TStringStream("");
IdHTTP->Post(LOGIN_PAGE,memo1->Lines,returnStr);
delete returnStr;
}catch(...){
ShowMessage("登录出错!");
}
hardtoreg 2003-04-02
  • 打赏
  • 举报
回复
运行后总出现下面的错误
XXXXXX.exe raised exception class EIDConnClosedGracefully with message
'Connection Closed Gracefully'.Process stopped .Use Step or Run to continue.
难道是request里的属性没有设定的原因?除了默认的属性外其余的我都没有设定。
一般应设定为什么内容?
dutousuan 2003-04-02
  • 打赏
  • 举报
回复
socket error应该是少了这一句
IdHTTP1->Request->ContentType = "application/x-www-form-urlencoded";
hardtoreg 2003-04-01
  • 打赏
  • 举报
回复
to Lo(水滴):
你真的试过吗?我把注册过的网站试了个遍。都不行。苦笑ing………………
代码:
void __fastcall TForm1::postClick(TObject *Sender)
{
TStrings *Source=new TStringList();
Source->Add(Edit2->Text);
Memo1->Text=IdHTTP1->Post(Edit1->Text,Source);
delete Source;
}
一般有两种结果:一种较温和,返回一段残缺不全的html脚本,可我用sniffer却没发现有http交互过程。另一种干脆就蹦出大红叉,说什么socket error。难道只能用socket了吗?郁闷啊!!!
不管怎么说,非常感谢!!!!
Lo 2003-04-01
  • 打赏
  • 举报
回复
如:要提交的http://www.url.com/login.asp?username=1111&password=2222

TStrings *Source=new TStringList();
Source->Add("username=1111");
Source->Add("password=2222");
Memo1->Text=IdHTTP1->Post("http://www.url.com/login.asp",Source);
delete Source;
ronshen 2003-03-31
  • 打赏
  • 举报
回复
例子没有,我的代码有点复杂,看起来也困难。

给你指条路吧,你用TMemoryStream,创建两个对象,一个TSourceStream,一个TReciveStream。
对source,先把我上边给的那个格式写成字符串,用stream的write方法写进去,其中的回车和content-disposition的格式如下:
const AnsiString CRLF = "\r\n";
const AnsiString CONTENT_DISPOSITION = "Content-Disposition: form-data; name=\"%s\"";

严格按照上边那个格式写,比如,"-------73847348"这个分隔符要一样,消息体结束的时候要加“--”。有几个字段,就这个格式往下填就行了。

在POST之前,把source stream的position指向0,然后POST,从recieve stream读东西。OK,搞定。
加载更多回复(6)

1,316

社区成员

发帖
与我相关
我的任务
社区描述
C++ Builder 网络及通讯开发
社区管理员
  • 网络及通讯开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧