代码之家  ›  专栏  ›  技术社区  ›  Issam Ali

使用delphi和tidhttp将数据发布到asp.net页面

  •  4
  • Issam Ali  · 技术社区  · 15 年前

    我有一个像这样简单的asp.net页面 http://issamsoft.com/app2/page1.aspx 我想发布一些数据并从响应中提取数据, 通过使用tidhttp。我在2009年的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.1500内部服务器错误)。 我在idhttp单元中读到了一些评论,它们讨论了http协议v 1.1的问题,如下所示:

    当前发布post时,idhttp会自动将协议设置为version 1.0独立于它最初的值,这是因为 有些服务器不完全尊重rfc。在 特别是,他们不尊重发送/不发送expect:100继续 标题。在找到不破坏rfc的最优解之前,我们 将文章限制为1.0版。

    我的代码有问题吗?还是tidhttp错误?如果问题出在 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值,但我省略了这一部分只是为了让我的示例mple和clear,因此当您想尝试上述代码时,必须从当前页面源获取viewstate、eventvalidation值。

    3 回复  |  直到 15 年前
        1
  •  4
  •   Jeroen Wiert Pluimers    15 年前

    这很可能是因为在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示例中发布的一样:

    _视图状态=%2FwPDwukmja3nje4mdcznmrk5%2Fc2wwvlab3l1wyzrpm3kzhrc0%3d&\u事件验证=%2Fwwwlxzuatauzrusgaoznisyoqy4vmuny6a8xi6ahqepi5q%3d&textbox1=issam&button1=按钮
    

    下面的示例现在给出了这个结果(请注意“ 伊萨姆 “在结果中):

    <!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代码反向工程为thttputibility类。

    这是最终代码:

    {$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.
    

    以及THTTputtility类:

    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.
    

    这会让你走的。

    ——杰罗恩

        2
  •  0
  •   mjn anonym    15 年前

    如果在tidhttp实例httpoptions属性中包含hokeeporigprotocol选项,它将不会返回到http 1.0。

    本文记录了您引用的注释文本下面的一些行:)

        // If hoKeepOrigProtocol is SET, is possible to assume that the developer
        // is sure in operations of the server
        if not (hoKeepOrigProtocol in FOptions) then begin
          FProtocolVersion := pv1_0;
        end;
    
        3
  •  -1
  •   jasonpenny    15 年前

    您需要更新viewstate和eventvalidation值。

    运行该代码会导致500个错误,但如果我打开页面并使用来自网页源的新的viewstate和eventvalidation值,则不再导致500个错误。

    我不知道为什么当德尔福的客户不工作的时候,C客户会工作,但也许它不再工作了?

    这看起来像是一个asp问题,而不是delphi。indy(tidhttp)和synapse都是delphi的好套接字库。