سؤال

لدينا متطلبات إرسال نموذج وحفظ بعض البيانات، ثم إعادة توجيه المستخدم إلى صفحة خارج الموقع، ولكن عند إعادة التوجيه، نحتاج إلى "إرسال" نموذج باستخدام POST، وليس GET.

كنت آمل أن تكون هناك طريقة سهلة لتحقيق ذلك، لكنني بدأت أعتقد أنه لا توجد طريقة.أعتقد أنه يجب علي الآن إنشاء صفحة أخرى بسيطة، بالنموذج الذي أريده فقط، وإعادة التوجيه إليه، وملء متغيرات النموذج، ثم إجراء استدعاء body.onload لبرنامج نصي يستدعي document.forms[0].submit( );

هل يمكن لأحد أن يقول لي إذا كان هناك بديل؟قد نحتاج إلى تعديل هذا لاحقًا في المشروع، وقد يصبح الأمر معقدًا نوعًا ما، لذلك إذا كانت هناك طريقة سهلة يمكننا القيام بها دون الاعتماد على أي صفحة أخرى، فسيكون ذلك رائعًا.

على أية حال، شكرا على أي وجميع الردود.

هل كانت مفيدة؟

المحلول

يتطلب القيام بذلك فهم كيفية عمل عمليات إعادة توجيه 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 الأكثر شيوعًا.

لذا، بقدر ما أعرف، الطريقة الوحيدة لتنفيذ شيء كهذا هي استخدام جافا سكريبت.هناك خياران يمكنني التفكير فيهما من أعلى رأسي:

  1. قم بإنشاء النموذج واحصل عليه action نقطة السمة إلى خادم الطرف الثالث.بعد ذلك، أضف حدث نقرة إلى زر الإرسال الذي ينفذ أولاً طلب AJAX إلى خادمك مع البيانات، ثم يسمح بإرسال النموذج إلى خادم الطرف الثالث.
  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، وقد ينجح ذلك أو لا ينجح.

وهذا ينبغي أن يجعل الحياة أسهل بكثير.يمكنك ببساطة استخدام طريقة 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 وهو خاصية "PostBackUrl" لأزرار ASP.يمكنك تعيينه على عنوان الصفحة التي تريد النشر فيها مباشرة، وعند النقر على هذا الزر، بدلاً من النشر مرة أخرى إلى نفس الصفحة كالمعتاد، فإنه بدلاً من ذلك يقوم بالنشر إلى الصفحة التي أشرت إليها.مفيد.تأكد من تعيين UseSubmitBehavior أيضًا على TRUE.

أعتقد أنه قد يكون من المثير للاهتمام مشاركة أن Heroku يفعل ذلك من خلال تسجيل الدخول الموحّد (SSO) الخاص به لموفري الوظائف الإضافية

يمكن رؤية مثال على كيفية عمله في مصدر أداة "kensa":

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

ويمكن رؤيته عمليًا إذا قمت بتشغيل جافا سكريبت.مثال لمصدر الصفحة:

<!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 الخاص بك للنشر على صفحة مختلفة.

إذا كنت بحاجة إلى القيام بذلك في codebehind، فجرب Server.Transfer.

@غير لامع،

لا يزال بإمكانك استخدام HttpWebRequest، ثم توجيه الاستجابة التي تتلقاها إلى استجابة تيار الإخراج الفعلي، وهذا من شأنه أن يخدم الاستجابة مرة أخرى للمستخدم.المشكلة الوحيدة هي أنه سيتم كسر أي عناوين URL نسبية.

ومع ذلك، قد ينجح ذلك.

إليك ما سأفعله:

ضع البيانات في نموذج قياسي (بدون سمة runat = "server") وقم بتعيين إجراء النموذج للنشر على الصفحة المستهدفة خارج الموقع.قبل الإرسال، أود إرسال البيانات إلى الخادم الخاص بي باستخدام XmlHttpRequest وتحليل الرد.إذا كان الرد يعني أنه يجب عليك المضي قدمًا في النشر خارج الموقع، فأنا (جافا سكريبت) سأواصل النشر وإلا سأعيد التوجيه إلى صفحة على موقعي

في PHP، يمكنك إرسال بيانات POST باستخدام cURL.هل هناك شيء مشابه لـ .NET؟

نعم، HttpWebRequest، راجع مشاركتي أدناه.

لا ينبغي أبدًا استخدام طريقة GET (و HEAD) لفعل أي شيء له آثار جانبية.قد يكون أحد الآثار الجانبية هو تحديث حالة تطبيق الويب، أو قد يؤدي إلى تحصيل الرسوم من بطاقتك الائتمانية.إذا كان الإجراء له آثار جانبية، فيجب استخدام طريقة أخرى (POST) بدلاً من ذلك.

لذلك، لا ينبغي أن يكون المستخدم (أو المتصفح الخاص به) مسؤولاً عن شيء ما قام به GET.إذا حدثت بعض الآثار الجانبية الضارة أو المكلفة نتيجة لـ GET، فسيكون ذلك خطأ تطبيق الويب، وليس المستخدم.وفقا للمواصفات، وكيل المستخدم لا يجب تتبع عملية إعادة التوجيه تلقائيًا ما لم تكن استجابة لطلب GET أو HEAD.

بالطبع، الكثير من طلبات GET لها بعض الآثار الجانبية، حتى لو كانت مجرد إلحاق بملف سجل.والشيء المهم هو أن التطبيق، وليس المستخدم، هو الذي يتحمل المسؤولية عن تلك التأثيرات.

الأقسام ذات الصلة بمواصفات HTTP هي 9.1.1 و9.1.2, ، و 10.3.

أقترح إنشاء HttpWebRequest لتنفيذ POST الخاص بك برمجيًا ثم إعادة التوجيه بعد قراءة الرد إن أمكن.

كود قابل للنسخ واللصق بناءً على طريقة بافلو نيمان

RedirectPost(string url, T bodyPayload) وGetPostData() مخصصان لأولئك الذين يريدون فقط تفريغ بعض البيانات المكتوبة بقوة في الصفحة المصدر وإعادتها مرة أخرى في الصفحة المستهدفة.يجب أن تكون البيانات قابلة للتسلسل بواسطة NewtonSoft Json.NET وتحتاج إلى الرجوع إلى المكتبة بالطبع.

ما عليك سوى النسخ واللصق في صفحتك (صفحاتك) أو الفئة الأساسية الأفضل لصفحاتك واستخدامها في أي مكان في تطبيقك.

قلبي يخرج منكم جميعًا الذين لا يزال يتعين عليهم استخدام نماذج الويب في عام 2019 لأي سبب كان.

        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;
        }

عادةً، كل ما ستحتاجه هو أن تحمل بعض الحالة بين هذين الطلبين.هناك في الواقع طريقة غير تقليدية للقيام بذلك والتي لا تعتمد على JavaScript (فكر في <noscript/>).

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

باستخدام ملف تعريف الارتباط هذا، يمكنك في الطلب التالي إلى /redirect.html استرداد معلومات الاسم = القيمة، ويمكنك تخزين أي نوع من المعلومات في سلسلة زوج الاسم/القيمة هذه، حتى 4 كيلو بايت من البيانات (الحد النموذجي لملفات تعريف الارتباط).بالطبع يجب عليك تجنب ذلك وتخزين رموز الحالة وبتات العلم بدلاً من ذلك.

عند تلقي هذا الطلب، تقوم في المقابل بالرد بطلب حذف لرمز الحالة هذا.

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

HTTP الخاص بي صدئ بعض الشيء، لقد قمت بزيارة RFC2109 وRFC2965 لمعرفة مدى موثوقية هذا الأمر، ويفضل أن أرغب في أن يقوم ملف تعريف الارتباط برحلة ذهابًا وإيابًا مرة واحدة بالضبط ولكن لا يبدو أن هذا ممكنًا أيضًا، قد تمثل ملفات تعريف الارتباط التابعة لجهات خارجية مشكلة بالنسبة لك إذا كنت تنتقل إلى مجال آخر.لا يزال هذا ممكنًا ولكنه ليس سهلاً كما هو الحال عندما تفعل أشياء داخل نطاقك الخاص.

المشكلة هنا هي التزامن، إذا كان مستخدم قوي يستخدم علامات تبويب متعددة وتمكن من تشذير بعض الطلبات التي تنتمي إلى نفس الجلسة (هذا غير مرجح جدًا، ولكنه ليس مستحيلًا) فقد يؤدي ذلك إلى تناقضات في تطبيقك.

إنها طريقة <noscript/> للقيام برحلات HTTP ذهابًا وإيابًا بدون عناوين URL وجافا سكريبت التي لا معنى لها

أقدم هذا الكود كأستاذ للمفهوم:إذا تم تشغيل هذا الرمز في سياق لست على دراية به، فأعتقد أنه يمكنك معرفة الجزء الذي يمثله.

الفكرة هي أن تقوم باستدعاء 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