Response.Redirect yerine POST ile yönlendirin


248

Bir form gönderme ve bazı verileri kaydetme, ardından kullanıcıyı site dışındaki bir sayfaya yönlendirme gereksinimimiz vardır, ancak yeniden yönlendirmede, bir formu GET yerine POST ile "göndermemiz" gerekir.

Bunu başarmanın kolay bir yolu olduğunu umuyordum, ama olmadığını düşünmeye başladım. Ben şimdi sadece istediğim formu ile basit bir başka sayfa oluşturmak gerekir, ona yönlendirmek, form değişkenleri doldurmak, sonra sadece document.forms [0] çağıran bir komut dosyasına bir body.onload çağrı yapmak gerekir. );

Alternatif olup olmadığını söyleyebilir mi? Bunu projede daha sonra değiştirmemiz gerekebilir ve bu biraz karmaşık olabilir, bu yüzden kolay olsaydı bunu diğer tüm sayfalara bağımlı olmayan harika olurdu.

Her neyse, her türlü yanıt için teşekkürler.


PHP'de, POST verilerini cURL ile gönderebilirsiniz. .NET için karşılaştırılabilir bir şey var mı?
Brian Warshaw

Bence aradığınız kolay cevap bu. Ne kadar dahice olduğuna inanamadım ... stackoverflow.com/a/6062248/110549
JoeCool

@BrianWarshaw System.Net.Http.HttpClient msdn.microsoft.com/en-us/library/… çok sezgisel ve kullanımı hızlı buluyorum .
Stoyan Dimov

Yanıtlar:


228

Bunun için HTTP yönlendirmelerinin nasıl çalıştığını anlamanız gerekir. Kullandığınızda , tarayıcıya bir sonraki adımın nereye gideceğini Response.Redirect()bildiren HTTP Durum Kodu 302 ile bir yanıt gönderirsiniz (istekte bulunan tarayıcıya). Tanım olarak, tarayıcı GET, orijinal istek bir olsa bile, bunu bir istek yoluyla yapar POST.

Başka bir seçenek, tarayıcının yönlendirme isteğini orijinal istekle aynı şekilde yapması gerektiğini belirten, ancak kullanıcıdan bir güvenlik uyarısı istemesi gerektiğini belirten HTTP Durum Kodu 307'yi kullanmaktır . Bunu yapmak için şöyle bir şey yazardınız:

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

Ne yazık ki, bu her zaman işe yaramaz. Farklı bir tarayıcı , bunu ortak bir durum kodu olmadığından farklı uygular .

Ne yazık ki, Opera ve FireFox geliştiricilerinin aksine, IE geliştiricileri hiçbir zaman spesifikasyonu okumamışlardır ve en son, en güvenli IE7 bile POST isteğini herhangi bir uyarı veya onay iletişim kutusu olmadan A alanından B alanına yönlendirecektir! Safari ayrıca ilginç bir şekilde davranır, ancak bir onay iletişim kutusu açmaz ve yönlendirmeyi gerçekleştirir, POST verilerini atar ve 307 yönlendirmesini daha yaygın olan 302'ye etkili bir şekilde değiştirir.

Bildiğim kadarıyla böyle bir şeyi uygulamanın tek yolu Javascript kullanmak olacaktır. Başımın üstünde düşünebileceğim iki seçenek var:

  1. Formu oluşturun ve action üçüncü taraf sunucuya özniteliği gösterin. Ardından, gönder düğmesine ilk olarak verilerle birlikte sunucunuzda bir AJAX isteği yürüten ve ardından formun üçüncü taraf sunucusuna gönderilmesine izin veren bir click olayı ekleyin.
  2. Sunucunuza göndermek için formu oluşturun. Form gönderildiğinde, kullanıcıya, tüm gizli girdilerde, iletmek istediğiniz tüm verileri içeren bir form içeren bir sayfa gösterin. "Yeniden yönlendiriliyor ..." gibi bir mesaj göstermeniz yeterlidir. Ardından, formu üçüncü taraf sunucuya gönderen sayfaya bir javascript olayı ekleyin.

İkisinden ikisini iki nedenden dolayı seçerdim. Birincisi, birinciden daha güvenilirdir, çünkü çalışması için Javascript gerekli değildir; etkinleştirilmemiş olanlar için gizli formun gönder düğmesini her zaman görünür hale getirebilir ve 5 saniyeden uzun sürerse bu düğmeye basmalarını isteyebilirsiniz. İkinci olarak, hangi verilerin üçüncü taraf sunucuya iletileceğine karar verebilirsiniz; sadece formu ilerledikçe işlerseniz, her zaman istediğiniz gibi olmayan tüm posta verilerini iletirsiniz. Tüm kullanıcılarınız için işe yaradığı varsayılarak 307 çözümü için aynıdır.

Bu yardımcı olur umarım!


1
Lütfen, "forma bir gönderme etkinliği eklemek" için "gönder düğmesine bir tıklama etkinliği ekle" seçeneğini değiştirin. Form göndermek için birden fazla yöntem vardır, örneğin, programlı gönderimden bağımsız olarak, herhangi bir metin girişi odaklı Enter tuşuna basmak.
temoto

122

Bu yaklaşımı kullanabilirsiniz:

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();

Sonuç olarak, istemci tüm html'yi sunucudan alır almaz , form gönderme ve tüm verileri tanımlı postbackUrl'e gönderen olay yükleme işlemi gerçekleşir.


9
+1 (eğer yapabilirsem sana daha fazla artıracağım). İşte cevap. Etkili ve noktaya. Hatta kafanızın üst kısmından bahsetmek yerine tüm kodları dahil ettiniz. Bunu aspx sayfamdaki bir iframe'de kullandım ve her şeyi mükemmel bir şekilde oluşturdu - yeniden yazma URL'leri yok. Harika iş! İframe kullanan kişiler için İPUCU: iframe'imi daha sonra bu kodu çalıştıran başka bir aspx sayfasına yönlendiriyorum.
MikeTeeVee

1
Çalışıyor! Gördüğüm tek kötü şey, tarayıcının geri düğmesine basarsanız, isteği tekrar yapar, böylece bir önceki sayfaya gerçekten erişemezsiniz. Bunun için bir çalışma alanı var mı?
Mt. Schneiders

4
Bunu yapmak için desteklenen yöntemin 307 kullanması için bir neden vardır. POST eylemleri, idempotent işlemler olarak tasarlanmıştır. Bu cevap sadece işe yarayan bir hack'tir ve gelecekte tarayıcılar tarafından kolayca engellenebilir.
Sam Rueby

2
@sam iyi bir nokta. Karşı bir argüman olarak: üst cevaba göre IE geliştirici 307 hakkında bile okumadı. Onu okuyan diğerleri yanlış uyguladı. Bu, 307'nin akıllıları (tarayıcı geliştiricilerinin akıllı olduğunu varsayarak) açıkça karıştırdığı ve yorumlama hatalarına eğilimli olduğu anlamına gelir. Yukarıdaki yaklaşım en azından benim için açıktır ve geçmiş ve şimdiki tüm tarayıcılarda çalışır. Biz geliştiriciler geçmişle (IE7 okuyun) ve günümüze giriş / çıkış savaşırken geleceğe dair çok endişeli değil. IMHO, herkes doğru anladığı için olduğu gibi tutmalı. Gelecekte, onu engellemenin mantığı ne olur?
so_mv

2
Bu TGHW'nin ikinci seçeneği ile nasıl etkili bir şekilde aynı değildir? Ayrıca, potansiyel olarak XSS güvenlik açığı oluşturmaya dikkat etmeyen birini de istemiyor musunuz?
Scott

33

Bunun için HttpWebRequest kullanılır.

Geri gönderirken, üçüncü tarafınıza bir HttpWebRequest oluşturun ve form verilerini gönderin, daha sonra bu yapıldıktan sonra, istediğiniz yere Response.Redirect yapabilirsiniz.

Üçüncü taraf formunu oluşturmak için tüm sunucu denetimlerinizi adlandırmanız gerekmediği ek avantaj elde edersiniz, POST dizesini oluştururken bu çeviriyi yapabilirsiniz.

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");

Ancak, kullanıcının bu formdaki yanıt sayfasını görmesini istiyorsanız, tek seçeneğiniz Server.Transfer kullanmaktır ve bu işe yarayıp yaramayabilir.


asp.net formundan daha fazla bir form istiyorsanız kullanacağım şeydir. Ben 3d güvenli ödeme için harici bir url bazı veri göndermek gerekiyor, o zaman ben isteği iade bilgi almak gerekir. Bunu yapmanın yolu bu mu? Teşekkür ederim
Barbaros Alp

Kullanıcının yanıtı görmesi gerekiyorsa, içeriği ve uygun başlıkları geri gönderebilirsiniz. Göreli kaynakların kullanımına bağlı olarak sonucun bazı yönlerini değiştirmeniz gerekebilir, ancak kesinlikle mümkündür.
Thomas S. Trias

6
Kullanıcının sunucudan gerçekleştiği için bu yöntemle iletilmeyen çerez verileri olabilir.
Scott

7

Bu hayatı daha kolay hale getirmelidir. Web uygulamanızda Response.RedirectWithData (...) yöntemini kolayca kullanabilirsiniz.

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

6

ASP.Net 3.5 yeni bir şey ASP düğmeleri bu "PostBackUrl" özelliğidir. Doğrudan göndermek istediğiniz sayfanın adresine ayarlayabilirsiniz ve bu düğme tıklatıldığında, normal gibi aynı sayfaya geri göndermek yerine, belirttiğiniz sayfaya mesaj gönderir. Kullanışlı. UseSubmitBehavior öğesinin de TRUE olarak ayarlandığından emin olun.


4

Heroku'nun bunu Eklenti sağlayıcıları için TOA ile paylaşmanın ilginç olabileceğini düşündüm

Nasıl çalıştığına bir örnek, kaynakta "kensa" aracının içindedir:

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

Ve eğer javascript çevirirseniz pratikte görülebilir. Örnek sayfa kaynağı:

<!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>

3

PostbackUrl, farklı bir sayfaya göndermek için asp düğmenizde ayarlanabilir.

bunu codebehind içinde yapmanız gerekiyorsa, Server.Transfer öğesini deneyin.


2

@Mat,

HttpWebRequest'i kullanmaya devam edebilir, ardından aldığınız yanıtı gerçek çıktı akışı yanıtına yönlendirebilirsiniz; bu, yanıtı kullanıcıya geri gönderir. Tek sorun, göreli URL'lerin kırılmasıdır.

Yine de, bu işe yarayabilir.


2

İşte yapacağım şey:

Verileri standart bir forma koyun (runat = "server" özniteliği olmadan) ve formun eylemini hedef site dışına gönderilecek şekilde ayarlayın. Göndermeden önce verileri bir XmlHttpRequest kullanarak sunucuma gönderir ve yanıtı analiz ederim . Yanıt, site dışı POSTing ile devam etmeniz gerektiği anlamına gelirse, ben (JavaScript) yayına devam eder, aksi takdirde sitemdeki bir sayfaya yönlendiririm


Bu işe yarar, ancak tüm ASP.NET sunucu tarafı işlevlerini kaybettiğinizi unutmayın.
senfo

2

PHP'de, POST verilerini cURL ile gönderebilirsiniz. .NET için karşılaştırılabilir bir şey var mı?

Evet, HttpWebRequest, aşağıdaki yazıma bakın.


2

GET (ve HEAD) yöntemi hiçbir zaman yan etkileri olan hiçbir şey için kullanılmamalıdır. Bir yan etki bir web uygulamasının durumunu güncelliyor olabilir veya kredi kartınızdan ücret alıyor olabilir. Bir eylemin yan etkileri varsa, bunun yerine başka bir yöntem (POST) kullanılmalıdır.

Bu nedenle, bir kullanıcı (veya tarayıcıları) bir GET tarafından yapılan bir şeyden sorumlu tutulmamalıdır. Bir GET'in sonucu olarak bazı zararlı veya pahalı yan etkiler meydana gelirse, bu, web uygulamasının hatası olacaktır, kullanıcının değil. Spesifikasyona göre, bir kullanıcı aracısı bir GET veya HEAD isteğine yanıt olmadıkça otomatik olarak bir yönlendirmeyi takip etmemelidir .

Tabii ki, bir GET isteğinin bir günlük dosyasına eklenmiş olsa bile bazı yan etkileri vardır. Önemli olan, kullanıcının değil uygulamanın bu etkilerden sorumlu tutulmasıdır.

HTTP spesifikasyonunun ilgili bölümleri 9.1.1 ve 9.1.2 ve 10.3'tür .


1

POST'nizi programlı olarak yürütmek ve sonra Yanıt'ı okuduktan sonra yeniden yönlendirmek için bir HttpWebRequest oluşturmanızı öneririm.


1

Pavlo Neyman'ın yöntemine dayanan kopyalanabilir kod

RedirectPost (string url, T bodyPayload) ve GetPostData (), kaynak sayfaya güçlü bir şekilde yazılmış bazı verileri döküp hedef olana geri getirmek isteyenler içindir. Veriler NewtonSoft Json.NET tarafından serileştirilebilir olmalıdır ve elbette kütüphaneye başvurmanız gerekir.

Sadece sayfalarınıza kopyalayın veya sayfalarınız için daha iyi bir temel sınıf oluşturun ve uygulamanızın herhangi bir yerinde kullanın.

2019'da hala Web Formlarını kullanmak zorunda olan herkese kalbim gidiyor.

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

0

Tipik olarak, ihtiyacınız olan tek şey bu iki istek arasında bir durum taşımaktır. Aslında bunu JavaScript'e dayanmayan gerçekten eğlenceli bir yol var (<noscript /> düşünün).

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

Bu çerezle, /redirect.html ad / değer bilgisini almak için aşağıdaki istekte, bu ad / değer çifti dizesinde 4K veri (tipik çerez sınırı) kadar her türlü bilgiyi saklayabilirsiniz. Elbette bundan kaçınmalı ve bunun yerine durum kodlarını ve bayrak bitlerini saklamalısınız.

Bu isteği aldıktan sonra, size bu durum kodu için bir silme talebini yanıtlarsınız.

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

HTTP'm biraz paslı RFC2109 ve RFC2965 üzerinden bunun gerçekten ne kadar güvenilir olduğunu anlamaya çalışıyorum, tercihen çerezin tam olarak bir kez tur atmasını istiyorum, ancak bu mümkün değil, üçüncü taraf çerezleri de başka bir alan adına taşınıyorsanız sizin için sorun olabilir. Bu hala mümkündür, ancak kendi alan adınızda bir şeyler yaptığınız kadar ağrısız değildir.

Buradaki sorun eşzamanlılıktır, eğer bir güç kullanıcısı birden fazla sekme kullanıyorsa ve aynı oturuma ait birkaç isteği araya sokmayı başarırsa (bu çok olası değildir, ancak imkansız değildir) bu, uygulamanızda tutarsızlıklara yol açabilir.

Anlamsız URL'ler ve JavaScript olmadan HTTP gidiş-dönüş yolculukları yapmanın <noscript /> yolu

Kavramın bir prof olarak bu kodu sağlar: Eğer bu kod aşina olmadığınız bir bağlamda çalıştırılırsa, hangi parçanın ne olduğunu çalışabilirsiniz düşünüyorum.

Buradaki fikir, yönlendirdiğinizde bazı durumlarla yeniden konumlandır'ı çağırmanızdır ve yeniden konumlandırdığınız URL, verileri (varsa) almak için GetState'i çağırır.

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.