Pregunta

Tenemos el requisito de enviar un formulario y guardar algunos datos, luego redirigir al usuario a una página externa, pero al redirigir, debemos "enviar" un formulario con POST, no GET.

Esperaba que hubiera una manera fácil de lograr esto, pero estoy empezando a pensar que no la hay.Creo que ahora debo crear otra página simple, solo con el formulario que quiero, redirigir a ella, completar las variables del formulario y luego hacer una llamada body.onload a un script que simplemente llama a document.forms[0].submit( );

¿Alguien puede decirme si hay alguna alternativa?Es posible que necesitemos modificar esto más adelante en el proyecto, y podría volverse un poco complicado, por lo que si fuera fácil, podríamos hacer esto sin depender de ninguna otra página, sería fantástico.

De todos modos, gracias por todas y cada una de las respuestas.

¿Fue útil?

Solución

Para hacer esto, es necesario comprender cómo funcionan las redirecciones HTTP.cuando usas Response.Redirect(), envía una respuesta (al navegador que realizó la solicitud) con Código de estado HTTP 302, que le indica al navegador dónde ir a continuación.Por definición, el navegador lo hará a través de un GET solicitud, incluso si la solicitud original era una POST.

Otra opción es utilizar Código de estado HTTP 307, que especifica que el navegador debe realizar la solicitud de redireccionamiento de la misma manera que la solicitud original, pero para mostrar al usuario una advertencia de seguridad.Para hacer eso, escribirías algo como esto:

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

Desafortunadamente, esto no siempre funcionará. Diferentes navegadores implementan esto de manera diferente, ya que no es un código de estado común.

Lamentablemente, a diferencia de los desarrolladores de Opera y Firefox, los desarrolladores de IE nunca han leído la especificación, e incluso el IE7 más reciente y seguro redirigirá la solicitud POST del dominio A al dominio B sin advertencias ni cuadros de diálogo de confirmación.Safari también actúa de una manera interesante, si bien no genera un cuadro de diálogo de confirmación y realiza la redirección, descarta los datos POST. cambiando efectivamente la redirección 307 al 302 más común.

Entonces, hasta donde yo sé, la única forma de implementar algo como esto sería usar Javascript.Hay dos opciones que se me ocurren en la cabeza:

  1. Cree el formulario y tenga su action El atributo apunta al servidor de terceros.Luego, agregue un evento de clic al botón de enviar que primero ejecute una solicitud AJAX a su servidor con los datos y luego permita que el formulario se envíe al servidor de terceros.
  2. Cree el formulario para publicar en su servidor.Cuando se envíe el formulario, muéstrele al usuario una página que tenga un formulario con todos los datos que desea transmitir, todo en entradas ocultas.Simplemente muestra un mensaje como "Redireccionando...".Luego, agregue un evento de JavaScript a la página que envía el formulario al servidor de terceros.

De los dos elegiría el segundo, por dos motivos.Primero, es más confiable que el primero porque no se requiere Javascript para que funcione;Para aquellos que no lo tienen habilitado, siempre pueden hacer visible el botón de enviar para el formulario oculto e indicarles que lo presionen si demora más de 5 segundos.En segundo lugar, puede decidir qué datos se transmiten al servidor de terceros;Si usa simplemente procesar el formulario a medida que avanza, pasará todos los datos de la publicación, que no siempre son los que desea.Lo mismo ocurre con la solución 307, suponiendo que funcione para todos sus usuarios.

¡Espero que esto ayude!

Otros consejos

Puede utilizar este enfoque:

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

Como resultado, justo después de que el cliente obtenga todo el HTML del servidor, el evento cargar tiene lugar que activa el envío del formulario y publica todos los datos en la URL de devolución de datos definida.

Para ello se utiliza HttpWebRequest.

En la devolución de datos, cree una HttpWebRequest para su tercero y publique los datos del formulario, luego, una vez hecho esto, puede Response.Redirect donde desee.

Obtiene la ventaja adicional de que no tiene que nombrar todos los controles de su servidor para crear el formulario de terceros; puede hacer esta traducción al crear la cadena 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");

Sin embargo, si necesita que el usuario vea la página de respuesta de este formulario, su única opción es utilizar Server.Transfer, y eso puede funcionar o no.

Esto debería hacer la vida mucho más fácil.Simplemente puede utilizar el método Response.RedirectWithData(...) en su aplicación web fácilmente.

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

Algo nuevo en ASP.Net 3.5 es la propiedad "PostBackUrl" de los botones ASP.Puede configurarlo en la dirección de la página en la que desea publicar directamente y, cuando se hace clic en ese botón, en lugar de volver a publicar en la misma página como de costumbre, se publica en la página que ha indicado.Práctico.Asegúrese de que UseSubmitBehavior también esté configurado en VERDADERO.

Pensé que sería interesante compartir que heroku hace esto con su SSO para proveedores de complementos.

Se puede ver un ejemplo de cómo funciona en el código fuente de la herramienta "kensa":

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

Y se puede ver en la práctica si desactivas javascript.Fuente de página de ejemplo:

<!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 se puede configurar en su botón ASP para publicar en una página diferente.

Si necesita hacerlo en código subyacente, pruebe Server.Transfer.

@Mate,

Aún puede usar HttpWebRequest y luego dirigir la respuesta que recibe a la respuesta del flujo de salida real; esto devolvería la respuesta al usuario.El único problema es que cualquier URL relativa estaría rota.

Aún así, eso puede funcionar.

Esto es lo que haría:

Coloque los datos en un formulario estándar (sin atributo runat="server") y configure la acción del formulario para publicar en la página externa de destino.Antes de enviar, enviaría los datos a mi servidor. usando una XmlHttpRequest y analizar la respuesta.Si la respuesta significa que debe continuar con la PUBLICACIÓN fuera del sitio, entonces yo (JavaScript) continuaría con la publicación; de lo contrario, lo redireccionaría a una página de mi sitio.

En PHP, puedes enviar datos POST con cURL.¿Existe algo comparable para .NET?

Sí, HttpWebRequest, vea mi publicación a continuación.

El método GET (y HEAD) nunca debe usarse para hacer nada que tenga efectos secundarios.Un efecto secundario podría ser la actualización del estado de una aplicación web o podría ser un cargo en su tarjeta de crédito.Si una acción tiene efectos secundarios, se debe utilizar otro método (POST).

Por lo tanto, un usuario (o su navegador) no debería ser responsable de algo hecho por un GET.Si se produjera algún efecto secundario dañino o costoso como resultado de un GET, sería culpa de la aplicación web, no del usuario.Según la especificación, un agente de usuario no debe sigue automáticamente una redirección a menos que sea una respuesta a una solicitud GET o HEAD.

Por supuesto, muchas solicitudes GET tienen algunos efectos secundarios, incluso si solo se agregan a un archivo de registro.Lo importante es que la aplicación, no el usuario, sea la responsable de esos efectos.

Las secciones relevantes de la especificación HTTP son 9.1.1 y 9.1.2, y 10.3.

Sugiero crear una HttpWebRequest para ejecutar programáticamente su POST y luego redirigir después de leer la Respuesta, si corresponde.

Código que se puede copiar y pegar basado en El método de Pavlo Neyman

RedirectPost(string url, T bodyPayload) y GetPostData() son para aquellos que solo quieren volcar algunos datos fuertemente tipados en la página de origen y recuperarlos en la de destino.Los datos deben ser serializables por NewtonSoft Json.NET y, por supuesto, es necesario hacer referencia a la biblioteca.

Simplemente copie y pegue en su(s) página(s) o, mejor aún, en la clase base para sus páginas y utilícela en cualquier lugar de su aplicación.

Mi corazón está con todos aquellos que todavía tienen que usar Web Forms en 2019 por cualquier 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;
        }

Normalmente, todo lo que necesitará es llevar algún estado entre estas dos solicitudes.De hecho, existe una forma realmente original de hacer esto que no depende de JavaScript (piense en <noscript/>).

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

Con esa cookie allí, puede en la siguiente solicitud /redirect.html recuperar la información nombre=valor, puede almacenar cualquier tipo de información en esta cadena de par nombre/valor, hasta decir 4K de datos (límite de cookies típico).Por supuesto, deberías evitar esto y, en su lugar, almacenar códigos de estado y bits de bandera.

Al recibir esta solicitud, usted a cambio responde con una solicitud de eliminación para ese código de estado.

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

Mi HTTP está un poco oxidado. He estado revisando RFC2109 y RFC2965 para determinar qué tan confiable es realmente. Preferiblemente, me gustaría que la cookie realice un viaje de ida y vuelta exactamente una vez, pero eso tampoco parece ser posible. Las cookies de terceros pueden ser un problema para usted si se muda a otro dominio.Esto todavía es posible, pero no tan sencillo como cuando haces cosas dentro de tu propio dominio.

El problema aquí es la concurrencia, si un usuario avanzado utiliza varias pestañas y logra intercalar un par de solicitudes que pertenecen a la misma sesión (esto es muy poco probable, pero no imposible), esto puede generar inconsistencias en su aplicación.

Es la forma <noscript/> de realizar viajes de ida y vuelta HTTP sin URL ni JavaScript sin sentido.

Proporciono este código como profesor de concepto:Si este código se ejecuta en un contexto con el que no está familiarizado, creo que puede averiguar qué parte es qué.

La idea es que llames a Relocate con algún estado cuando redirecciones, y la URL que reubicas llama a GetState para obtener los datos (si los hay).

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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top