質問

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

これを簡単に達成する方法があることを期待していましたが、それはないと思い始めています。ここで、必要なフォームだけを含む単純な別のページを作成し、そこにリダイレクトし、フォーム変数を設定してから、単に document.forms[0].submit( を呼び出すだけのスクリプトへの body.onload 呼び出しを実行する必要があると思います。 );

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

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

役に立ちましたか?

解決

これを行うには、HTTP リダイレクトがどのように機能するかを理解する必要があります。使用するときは Response.Redirect(), 、(リクエストを行ったブラウザに) 応答を送信します。 HTTPステータスコード302, 、ブラウザに次にどこに行くかを指示します。定義上、ブラウザは、 GET 元のリクエストが POST.

別のオプションを使用することもできます 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 データを破棄します。 307 リダイレクトをより一般的な 302 に効果的に変更します。

したがって、私の知る限り、このようなものを実装する唯一の方法は Javascript を使用することです。思いつく限りの選択肢は 2 つあります。

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

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

お役に立てれば!

他のヒント

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

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 を取得した直後にイベントが発生します オンロード これにより、フォームの送信がトリガーされ、すべてのデータが定義された postbackUrl に送信されます。

これには 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 を利用することですが、これは機能する場合と機能しない場合があります。

これで生活がずっと楽になるはずです。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

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

Heroku がアドオンプロバイダーへの SSO を使用してこれを行うことを共有するのは興味深いかもしれないと思いました

どのように動作するかの例は、「kensa」ツールのソースで見ることができます。

https://github.com/heroku/kensa/blob/d4a56d50dcbebc2d26a4950081acda988937ee10/lib/heroku/kensa/post_proxy.rb

JavaScript をオフにすると実際に見ることができます。ページソースの例:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Heroku Add-ons SSO</title>
  </head>

  <body>
    <form method="POST" action="https://XXXXXXXX/sso/login">

        <input type="hidden" name="email" value="XXXXXXXX" />

        <input type="hidden" name="app" value="XXXXXXXXXX" />

        <input type="hidden" name="id" value="XXXXXXXX" />

        <input type="hidden" name="timestamp" value="1382728968" />

        <input type="hidden" name="token" value="XXXXXXX" />

        <input type="hidden" name="nav-data" value="XXXXXXXXX" />

    </form>

    <script type="text/javascript">
      document.forms[0].submit();
    </script>
  </body>
</html>

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

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

@マット、

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

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

私ならこうします:

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

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

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

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

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

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

HTTP 仕様の関連セクションは次のとおりです。 9.1.1および9.1.2, 、 そして 10.3.

HttpWebRequest を構築してプログラムで POST を実行し、該当する場合は応答を読んだ後にリダイレクトすることをお勧めします。

に基づくコピー&ペースト可能なコード パブロ・ネイマンの手法

RedirectPost(string url, T bodyPayload) と GetPostData() は、ソース ページ内の厳密に型指定されたデータをダンプし、それをターゲット ページにフェッチしたいだけの人向けです。データは NewtonSoft Json.NET でシリアル化できる必要があり、もちろんライブラリを参照する必要があります。

ページまたはページの基本クラスにコピー&ペーストするだけで、アプリケーションのどこでも使用できます。

何らかの理由で、2019 年になっても Web フォームを使用しなければならない皆様に心よりお見舞い申し上げます。

        protected void RedirectPost(string url, IEnumerable<KeyValuePair<string,string>> fields)
        {
            Response.Clear();

            const string template =
@"<html>
<body onload='document.forms[""form""].submit()'>
<form name='form' action='{0}' method='post'>
{1}
</form>
</body>
</html>";

            var fieldsSection = string.Join(
                    Environment.NewLine,
                    fields.Select(x => $"<input type='hidden' name='{HttpUtility.UrlEncode(x.Key)}' value='{HttpUtility.UrlEncode(x.Value)}'>")
                );

            var html = string.Format(template, HttpUtility.UrlEncode(url), fieldsSection);

            Response.Write(html);

            Response.End();
        }

        private const string JsonDataFieldName = "_jsonData";

        protected void RedirectPost<T>(string url, T bodyPayload)
        {
            var json = JsonConvert.SerializeObject(bodyPayload, Formatting.Indented);
            //explicit type declaration to prevent recursion
            IEnumerable<KeyValuePair<string, string>> postFields = new List<KeyValuePair<string, string>>()
                {new KeyValuePair<string, string>(JsonDataFieldName, json)};

            RedirectPost(url, postFields);

        }

        protected T GetPostData<T>() where T: class 
        {
            var urlEncodedFieldData = Request.Params[JsonDataFieldName];
            if (string.IsNullOrEmpty(urlEncodedFieldData))
            {
                return null;// default(T);
            }

            var fieldData = HttpUtility.UrlDecode(urlEncodedFieldData);

            var result = JsonConvert.DeserializeObject<T>(fieldData);
            return result;
        }

通常、必要なのは、これら 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;
}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top