268

フォーム送信を取得してデータを保存し、ユーザーをオフサイトのページにリダイレクトする必要がありますが、リダイレクトでは、GET ではなく POST でフォームを「送信」する必要があります。

これを達成する簡単な方法があることを望んでいましたが、そうではないと考え始めています。必要なフォームだけで簡単な他のページを作成し、それにリダイレクトし、フォーム変数を入力してから、 document.forms[0].submit( を呼び出すだけのスクリプトに body.onload 呼び出しを行う必要があると思います。 );

代替手段があるかどうか誰か教えてもらえますか? プロジェクトの後半でこれを微調整する必要があり、複雑になる可能性があるため、他のページに依存しない簡単な方法があれば、素晴らしいことです。

とにかく、すべての応答に感謝します。

4

14 に答える 14

231

これを行うには、HTTPリダイレクトがどのように機能するかを理解する必要があります。を使用すると、 HTTPステータスコード302Response.Redirect()を使用して(リクエストを行ったブラウザに)応答を送信します。これにより、ブラウザに次に進む場所が通知されます。定義上、元のリクエストがであったとしても、ブラウザはリクエストを介してそれを行います。GETPOST

もう1つのオプションは、HTTPステータスコード307を使用することです。これは、ブラウザが元のリクエストと同じ方法でリダイレクトリクエストを行う必要があることを指定しますが、セキュリティ警告をユーザーに表示します。これを行うには、次のように記述します。

public void PageLoad(object sender, EventArgs e)
{
    // Process the post on your side   

    Response.Status = "307 Temporary Redirect";
    Response.AddHeader("Location", "http://example.com/page/to/post.to");
}

残念ながら、これは常に機能するとは限りません。 これは一般的なステータスコードではないため、ブラウザによって実装方法が異なります。

残念ながら、OperaやFireFoxの開発者とは異なり、IEの開発者は仕様を読んだことがなく、最新の最も安全なIE7でさえ、警告や確認ダイアログなしでPOSTリクエストをドメインAからドメインBにリダイレクトします。Safariも興味深い方法で動作しますが、確認ダイアログを表示せずにリダイレクトを実行しますが、POSTデータを破棄し、リダイレクトをより一般的な302に効果的に変更します。

したがって、私が知る限り、このようなものを実装する唯一の方法は、Javascriptを使用することです。私が頭のてっぺんから考えることができる2つのオプションがあります:

  1. フォームを作成し、そのaction属性がサードパーティのサーバーを指すようにします。次に、送信ボタンにクリックイベントを追加します。このボタンは、最初にデータを使用してサーバーへのAJAXリクエストを実行し、次にフォームをサードパーティのサーバーに送信できるようにします。
  2. サーバーに投稿するフォームを作成します。フォームが送信されたら、渡したいすべてのデータがすべて非表示の入力で含まれているフォームを含むページをユーザーに表示します。「リダイレクトしています...」のようなメッセージを表示するだけです。次に、フォームをサードパーティサーバーに送信するページにjavascriptイベントを追加します。

2つのうち、2つ目を選択するのは、2つの理由からです。まず、Javascriptが機能する必要がないため、最初のものよりも信頼性が高くなります。有効にしていない場合は、いつでも非表示のフォームの送信ボタンを表示し、5秒以上かかる場合はそれを押すように指示できます。次に、サードパーティのサーバーに送信するデータを決定できます。フォームを処理するだけで使用すると、すべての投稿データが渡されることになりますが、これは必ずしも必要なものではありません。すべてのユーザーに対して機能すると仮定すると、307ソリューションについても同じです。

お役に立てれば!

于 2008-09-06T18:54:00.723 に答える
133

このアプローチを使用できます:

Response.Clear();

StringBuilder sb = new StringBuilder();
sb.Append("<html>");
sb.AppendFormat(@"<body onload='document.forms[""form""].submit()'>");
sb.AppendFormat("<form name='form' action='{0}' method='post'>",postbackUrl);
sb.AppendFormat("<input type='hidden' name='id' value='{0}'>", id);
// Other params go here
sb.Append("</form>");
sb.Append("</body>");
sb.Append("</html>");

Response.Write(sb.ToString());

Response.End();

その結果、クライアントがサーバーからすべての html を取得した直後に、フォームの送信をトリガーするイベントonloadが発生し、定義された postbackUrl にすべてのデータがポストされます。

于 2010-05-10T13:16:31.040 に答える
33

これには HttpWebRequest が使用されます。

ポストバック時にサード パーティに HttpWebRequest を作成し、フォーム データをポストすると、必要な場所に Response.Redirect を送信できます。

サード パーティのフォームを作成するためにすべてのサーバー コントロールに名前を付ける必要がないという追加の利点が得られます。POST 文字列を作成するときにこの変換を行うことができます。

string url = "3rd Party Url";

StringBuilder postData = new StringBuilder();

postData.Append("first_name=" + HttpUtility.UrlEncode(txtFirstName.Text) + "&");
postData.Append("last_name=" + HttpUtility.UrlEncode(txtLastName.Text));

//ETC for all Form Elements

// Now to Send Data.
StreamWriter writer = null;

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";                        
request.ContentLength = postData.ToString().Length;
try
{
    writer = new StreamWriter(request.GetRequestStream());
    writer.Write(postData.ToString());
}
finally
{
    if (writer != null)
        writer.Close();
}

Response.Redirect("NewPage");

ただし、ユーザーにこのフォームからの応答ページを表示する必要がある場合は、Server.Transfer を使用するしかありません。それが機能する場合と機能しない場合があります。

于 2008-09-05T19:33:34.940 に答える
7

ASP.Net 3.5の新しい点は、ASPボタンのこの「PostBackUrl」プロパティです。直接投稿したいページのアドレスに設定でき、そのボタンをクリックすると、通常のように同じページに投稿するのではなく、指定したページに投稿します。ハンディ。UseSubmitBehaviorもTRUEに設定されていることを確認してください。

于 2010-05-13T07:15:38.987 に答える
7

これは人生をはるかに楽にするはずです。WebアプリケーションでResponse.RedirectWithData(...)メソッドを簡単に使用できます。

Imports System.Web
Imports System.Runtime.CompilerServices

Module WebExtensions

    <Extension()> _
    Public Sub RedirectWithData(ByRef aThis As HttpResponse, ByVal aDestination As String, _
                                ByVal aData As NameValueCollection)
        aThis.Clear()
        Dim sb As StringBuilder = New StringBuilder()

        sb.Append("<html>")
        sb.AppendFormat("<body onload='document.forms[""form""].submit()'>")
        sb.AppendFormat("<form name='form' action='{0}' method='post'>", aDestination)

        For Each key As String In aData
            sb.AppendFormat("<input type='hidden' name='{0}' value='{1}' />", key, aData(key))
        Next

        sb.Append("</form>")
        sb.Append("</body>")
        sb.Append("</html>")

        aThis.Write(sb.ToString())

        aThis.End()
    End Sub

End Module
于 2012-08-29T09:44:53.200 に答える
3

@マット、

引き続き HttpWebRequest を使用して、受け取った応答を実際の出力ストリーム応答に向けることができます。これにより、応答がユーザーに返されます。唯一の問題は、相対 URL が壊れることです。

それでも、それはうまくいくかもしれません。

于 2008-09-05T21:15:38.470 に答える
3

PostbackUrl を ASP ボタンに設定して、別のページに投稿できます。

コードビハインドでそれを行う必要がある場合は、Server.Transfer を試してください。

于 2008-09-05T19:11:43.933 に答える
2

GET(およびHEAD)メソッドは、副作用のあることを行うために使用しないでください。副作用として、Webアプリケーションの状態が更新されたり、クレジットカードに請求されたりする場合があります。アクションに副作用がある場合は、代わりに別の方法(POST)を使用する必要があります。

したがって、ユーザー(またはそのブラウザー)は、GETによって行われたことに対して責任を負わされるべきではありません。GETの結果として有害または高価な副作用が発生した場合、それはユーザーではなくWebアプリケーションの障害になります。仕様によると、ユーザーエージェントは、GETまたはHEADリクエストへの応答でない限り、リダイレクトを自動的にたどってはなりません。

もちろん、ログファイルに追加するだけの場合でも、多くのGETリクエストにはいくつかの副作用があります。重要なことは、ユーザーではなくアプリケーションがこれらの影響に責任を持つべきであるということです。

HTTP仕様の関連セクションは、9.1.1と9.1.2、および10.3です。

于 2008-09-08T22:02:44.107 に答える
2

HttpWebRequest を作成して POST をプログラムで実行し、該当する場合は応答を読み取った後にリダイレクトすることをお勧めします。

于 2008-09-05T19:20:41.977 に答える
2

PHP では、cURL を使用して POST データを送信できます。.NET に匹敵するものはありますか?

はい、HttpWebRequest です。以下の私の投稿を参照してください。

于 2008-09-06T21:02:26.967 に答える
2

これが私がすることです:

データを標準フォーム (runat="server" 属性なし) に配置し、フォームのアクションをターゲットのオフサイト ページに投稿するように設定します。送信する前に、XmlHttpRequest を使用してサーバーにデータを送信し、応答を分析します。応答がオフサイト POST を続行する必要があることを意味する場合、私 (JavaScript) は投稿を続行します。それ以外の場合は、自分のサイトのページにリダイレクトします。

于 2008-09-06T17:06:20.030 に答える
1

通常、必要なのは、これら 2 つのリクエスト間で何らかの状態を保持することだけです。これを行うには、JavaScript に依存しない非常にファンキーな方法があります (<noscript/> を考えてください)。

Set-Cookie: name=value; Max-Age=120; Path=/redirect.html

その Cookie を使用すると、/redirect.html への次のリクエストで name=value 情報を取得できます。この名前と値のペアの文字列には、最大 4K のデータ (通常の Cookie 制限) まで、あらゆる種類の情報を格納できます。もちろん、これは避けて、代わりにステータス コードとフラグ ビットを保存する必要があります。

このリクエストを受け取ったら、そのステータス コードの削除リクエストで応答します。

Set-Cookie: name=value; Max-Age=0; Path=/redirect.html

私の HTTP は少し錆びています。RFC2109 と RFC2965 を調べて、これが実際にどれほど信頼できるかを調べてきました。できれば、Cookie を正確に 1 回往復させたいのですが、それは可能ではないようです。また、サードパーティの Cookie別のドメインに移転する場合、これが問題になる可能性があります。これはまだ可能ですが、独自のドメイン内で何かをしている場合ほど簡単ではありません。

ここでの問題は同時実行性です。パワー ユーザーが複数のタブを使用していて、同じセッションに属するいくつかのリクエストをインターリーブすることができた場合 (これはほとんどありませんが、不可能ではありません)、アプリケーションに矛盾が生じる可能性があります。

無意味な URL や JavaScript を使用せずに HTTP ラウンド トリップを行う <noscript/> 方法です。

このコードを概念の教授として提供します。このコードがよく知られていないコンテキストで実行される場合、どの部分が何であるかを理解できると思います。

リダイレクト時に何らかの状態で Relocate を呼び出し、再配置した URL が GetState を呼び出してデータ (存在する場合) を取得するという考え方です。

const string StateCookieName = "state";

static int StateCookieID;

protected void Relocate(string url, object state)
{
    var key = "__" + StateCookieName + Interlocked
        .Add(ref StateCookieID, 1).ToInvariantString();

    var absoluteExpiration = DateTime.Now
        .Add(new TimeSpan(120 * TimeSpan.TicksPerSecond));

    Context.Cache.Insert(key, state, null, absoluteExpiration,
        Cache.NoSlidingExpiration);

    var path = Context.Response.ApplyAppPathModifier(url);

    Context.Response.Cookies
        .Add(new HttpCookie(StateCookieName, key)
        {
            Path = path,
            Expires = absoluteExpiration
        });

    Context.Response.Redirect(path, false);
}

protected TData GetState<TData>()
    where TData : class
{
    var cookie = Context.Request.Cookies[StateCookieName];
    if (cookie != null)
    {
        var key = cookie.Value;
        if (key.IsNonEmpty())
        {
            var obj = Context.Cache.Remove(key);

            Context.Response.Cookies
                .Add(new HttpCookie(StateCookieName)
                { 
                    Path = cookie.Path, 
                    Expires = new DateTime(1970, 1, 1) 
                });

            return obj as TData;
        }
    }
    return null;
}
于 2009-08-30T11:50:35.643 に答える