Domanda

Abbiamo l'obbligo di inviare un modulo e salvare alcuni dati, quindi reindirizzare l'utente a una pagina fuori sito, ma nel reindirizzamento dobbiamo "inviare" un modulo con POST, non GET.

Speravo ci fosse un modo semplice per raggiungere questo obiettivo, ma inizio a pensare che non ci sia.Penso che ora devo creare un'altra pagina semplice, con solo il modulo che desidero, reindirizzarla ad essa, popolare le variabili del modulo, quindi eseguire una chiamata body.onload a uno script che chiama semplicemente document.forms[0].submit( );

Qualcuno sa dirmi se c'è un'alternativa?Potremmo aver bisogno di modificarlo più avanti nel progetto, e potrebbe diventare un po' complicato, quindi se ci fosse un modo semplice potessimo farlo senza dipendere da altre pagine, sarebbe fantastico.

Comunque grazie per tutte le risposte.

È stato utile?

Soluzione

Per fare ciò è necessario comprendere come funzionano i reindirizzamenti HTTP.Quando usi Response.Redirect(), invii una risposta (al browser che ha effettuato la richiesta) con Codice di stato HTTP 302, che indica al browser dove andare successivamente.Per definizione, il browser lo farà tramite a GET richiesta, anche se la richiesta originale era a POST.

Un'altra opzione è usare Codice di stato HTTP 307, che specifica che il browser deve effettuare la richiesta di reindirizzamento nello stesso modo della richiesta originale, ma avvisando l'utente con un avviso di sicurezza.Per fare ciò, dovresti scrivere qualcosa del genere:

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

Sfortunatamente, questo non funzionerà sempre. Diversi browser lo implementano in modo diverso, poiché non è un codice di stato comune.

Purtroppo, a differenza degli sviluppatori di Opera e FireFox, gli sviluppatori di IE non hanno mai letto le specifiche e anche l'ultimo e più sicuro IE7 reindirizzerà la richiesta POST dal dominio A al dominio B senza alcun avviso o finestra di dialogo di conferma!Anche Safari si comporta in modo interessante, anche se non apre una finestra di dialogo di conferma ed esegue il reindirizzamento, butta via i dati POST, cambiando effettivamente il reindirizzamento 307 nel più comune 302.

Quindi, per quanto ne so, l'unico modo per implementare qualcosa del genere sarebbe utilizzare Javascript.Ci sono due opzioni a cui riesco a pensare subito:

  1. Crea il modulo e ottieni il suo action l'attributo punta al server di terze parti.Quindi, aggiungi un evento clic al pulsante di invio che esegue prima una richiesta AJAX al tuo server con i dati e quindi consente l'invio del modulo al server di terze parti.
  2. Crea il modulo da pubblicare sul tuo server.Quando il modulo viene inviato, mostra all'utente una pagina che contiene un modulo con tutti i dati che desideri trasmettere, il tutto in input nascosti.Basta mostrare un messaggio come "Reindirizzamento...".Quindi, aggiungi un evento JavaScript alla pagina che invia il modulo al server di terze parti.

Tra i due sceglierei il secondo, per due motivi.Innanzitutto è più affidabile del primo perché non è necessario Javascript per funzionare;per coloro che non l'hanno abilitato, puoi sempre rendere visibile il pulsante di invio per il modulo nascosto e chiedergli di premerlo se impiega più di 5 secondi.In secondo luogo, puoi decidere quali dati trasmettere al server di terze parti;se usi semplicemente l'elaborazione del modulo man mano che scorre, passerai tutti i dati del post, il che non è sempre quello che desideri.Lo stesso vale per la soluzione 307, presupponendo che funzioni per tutti i tuoi utenti.

Spero che questo ti aiuti!

Altri suggerimenti

Puoi utilizzare questo approccio:

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

Di conseguenza, subito dopo che il client riceverà tutto l'HTML dal server dell'evento caricare avviene che attiva l'invio del modulo e pubblica tutti i dati nel postbackUrl definito.

A questo scopo viene utilizzato HttpWebRequest.

Durante il postback, crea una HttpWebRequest per la tua terza parte e pubblica i dati del modulo, quindi una volta fatto ciò, puoi Response.Redirect dove vuoi.

Ottieni l'ulteriore vantaggio di non dover nominare tutti i controlli del server per creare il modulo di terze parti, puoi eseguire questa traduzione durante la creazione della stringa 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");

Tuttavia, se hai bisogno che l'utente veda la pagina di risposta da questo modulo, l'unica opzione è utilizzare Server.Transfer e potrebbe funzionare o meno.

Questo dovrebbe rendere la vita molto più semplice.Puoi semplicemente utilizzare facilmente il metodo Response.RedirectWithData(...) nella tua applicazione web.

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

Qualcosa di nuovo in ASP.Net 3.5 è questa proprietà "PostBackUrl" dei pulsanti ASP.Puoi impostarlo sull'indirizzo della pagina in cui desideri pubblicare direttamente e, quando si fa clic su quel pulsante, invece di pubblicare nuovamente sulla stessa pagina come al solito, pubblica invece sulla pagina che hai indicato.Maneggevole.Assicurati che anche UseSubmitBehavior sia impostato su TRUE.

Ho pensato che potesse essere interessante condividere che Heroku fa questo con il suo SSO ai fornitori di componenti aggiuntivi

Un esempio di come funziona può essere visto nel sorgente dello strumento "kensa":

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

E può essere visto in pratica se si attiva Javascript.Sorgente della pagina di esempio:

<!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 può essere impostato sul pulsante ASP per pubblicare su una pagina diversa.

se devi farlo in codebehind, prova Server.Transfer.

@Opaco,

Puoi comunque utilizzare HttpWebRequest, quindi indirizzare la risposta che ricevi alla risposta effettiva del flusso di output, questo restituirebbe la risposta all'utente.L'unico problema è che qualsiasi URL relativo verrebbe interrotto.

Tuttavia, potrebbe funzionare.

Ecco cosa farei:

Inserisci i dati in un modulo standard (senza attributo runat="server") e imposta l'azione del modulo per pubblicarli sulla pagina fuori sito di destinazione.Prima di inviare, invierei i dati al mio server utilizzando un XmlHttpRequest e analizzare la risposta.Se la risposta significa che dovresti procedere con il POST fuori sito, allora io (JavaScript) procederei con il post altrimenti reindirizzerei a una pagina sul mio sito

In PHP, puoi inviare dati POST con cURL.Esiste qualcosa di paragonabile per .NET?

Sì, HttpWebRequest, vedi il mio post qui sotto.

Il metodo GET (e HEAD) non dovrebbe mai essere usato per fare qualcosa che abbia effetti collaterali.Un effetto collaterale potrebbe essere l'aggiornamento dello stato di un'applicazione web o l'addebito sulla tua carta di credito.Se un'azione ha effetti collaterali, è necessario utilizzare invece un altro metodo (POST).

Pertanto, un utente (o il suo browser) non dovrebbe essere ritenuto responsabile per qualcosa fatto da un GET.Se a seguito di un GET si verificassero effetti collaterali dannosi o costosi, la colpa sarebbe dell'applicazione web, non dell'utente.Secondo le specifiche, un agente utente non devi segue automaticamente un reindirizzamento a meno che non si tratti di una risposta a una richiesta GET o HEAD.

Naturalmente, molte richieste GET hanno alcuni effetti collaterali, anche se si limitano ad aggiungerle a un file di registro.L'importante è che sia l'applicazione, e non l'utente, ad essere ritenuta responsabile di tali effetti.

Le sezioni pertinenti delle specifiche HTTP sono 9.1.1 e 9.1.2, E 10.3.

Suggerisco di creare un HttpWebRequest per eseguire a livello di codice il tuo POST e quindi reindirizzarlo dopo aver letto la risposta, se applicabile.

Codice copia-incollabile basato su Il metodo di Pavlo Neyman

RedirectPost(string url, T bodyPayload) e GetPostData() sono per coloro che vogliono semplicemente scaricare alcuni dati fortemente tipizzati nella pagina di origine e recuperarli in quella di destinazione.I dati devono essere serializzabili da NewtonSoft Json.NET e ovviamente è necessario fare riferimento alla libreria.

Basta copiare e incollare nelle tue pagine o, meglio ancora, nella classe base per le tue pagine e usarla ovunque nella tua applicazione.

Il mio pensiero va a tutti voi che dovete ancora utilizzare i moduli Web nel 2019 per qualsiasi motivo.

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

In genere, tutto ciò di cui avrai bisogno è mantenere uno stato tra queste due richieste.In realtà esiste un modo davvero originale per farlo che non si basa su JavaScript (pensa a <noscript/>).

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

Con quel cookie lì, puoi nella seguente richiesta a /redirect.html recuperare le informazioni nome=valore, puoi memorizzare qualsiasi tipo di informazione in questa stringa di coppia nome/valore, fino a dire 4K di dati (limite tipico dei cookie).Ovviamente dovresti evitarlo e memorizzare invece codici di stato e bit di flag.

Dopo aver ricevuto questa richiesta, in cambio rispondi con una richiesta di eliminazione per quel codice di stato.

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

Il mio HTTP è un po' arrugginito, ho esaminato RFC2109 e RFC2965 per capire quanto sia affidabile, preferibilmente vorrei che il cookie facesse un giro esattamente una volta, ma anche questo non sembra essere possibile, i cookie di terze parti potrebbero rappresentare un problema per te se ti trasferisci in un altro dominio.Questo è ancora possibile ma non così indolore come quando fai cose all'interno del tuo dominio.

Il problema qui è la concorrenza, se un utente esperto utilizza più schede e riesce a intercalare un paio di richieste appartenenti alla stessa sessione (questo è molto improbabile, ma non impossibile) ciò potrebbe portare a incoerenze nella tua applicazione.

È il modo <noscript/> di eseguire viaggi di andata e ritorno HTTP senza URL e JavaScript privi di significato

Fornisco questo codice come professore di concetto:Se questo codice viene eseguito in un contesto con cui non hai familiarità, penso che tu possa capire quale parte è cosa.

L'idea è che chiami Relocate con uno stato quando esegui il reindirizzamento e l'URL che hai trasferito chiama GetState per ottenere i dati (se presenti).

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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top