Pergunta

Temos o requisito de enviar um formulário e salvar alguns dados, depois redirecionar o usuário para uma página externa, mas no redirecionamento, precisamos "enviar" um formulário com POST, não GET.

Eu esperava que houvesse uma maneira fácil de conseguir isso, mas estou começando a pensar que não existe.Acho que agora devo criar uma outra página simples, apenas com o formulário que desejo, redirecioná-la, preencher as variáveis ​​do formulário e, em seguida, fazer uma chamada body.onload para um script que apenas chama document.forms[0].submit( );

Alguém pode me dizer se existe uma alternativa?Talvez precisemos ajustar isso mais tarde no projeto, e pode ficar um pouco complicado, então, se houvesse uma maneira fácil, poderíamos fazer tudo isso não dependente de outras páginas, o que seria fantástico.

De qualquer forma, obrigado por toda e qualquer resposta.

Foi útil?

Solução

Fazer isso requer entender como funcionam os redirecionamentos HTTP.Quando você usa Response.Redirect(), você envia uma resposta (para o navegador que fez a solicitação) com Código de status HTTP 302, que informa ao navegador para onde ir em seguida.Por definição, o navegador fará isso através de um GET solicitação, mesmo que a solicitação original fosse uma POST.

Outra opção é usar Código de status HTTP 307, que especifica que o navegador deve fazer a solicitação de redirecionamento da mesma forma que a solicitação original, mas para avisar o usuário com um aviso de segurança.Para fazer isso, você escreveria algo assim:

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

Infelizmente, isso nem sempre funciona. Diferentes navegadores implementam isso de maneira diferente, uma vez que não é um código de status comum.

Infelizmente, ao contrário dos desenvolvedores do Opera e do FireFox, os desenvolvedores do IE nunca leram as especificações, e mesmo o IE7 mais recente e seguro redirecionará a solicitação POST do domínio A para o domínio B sem quaisquer avisos ou caixas de diálogo de confirmação!O Safari também atua de maneira interessante, embora não abra uma caixa de diálogo de confirmação e execute o redirecionamento, ele joga fora os dados do POST, mudando efetivamente o redirecionamento 307 para o 302 mais comum.

Então, até onde eu sei, a única forma de implementar algo assim seria usar Javascript.Existem duas opções que consigo pensar de cara:

  1. Crie o formulário e tenha seu action ponto de atributo para o servidor de terceiros.Em seguida, adicione um evento click ao botão enviar que primeiro executa uma solicitação AJAX ao seu servidor com os dados e, em seguida, permite que o formulário seja enviado ao servidor de terceiros.
  2. Crie o formulário para postar em seu servidor.Quando o formulário for enviado, mostre ao usuário uma página que contém um formulário com todos os dados que você deseja repassar, todos em entradas ocultas.Basta mostrar uma mensagem como "Redirecionando...".Em seguida, adicione um evento javascript à página que envia o formulário ao servidor de terceiros.

Dos dois, eu escolheria o segundo, por dois motivos.Primeiro, é mais confiável que o primeiro porque não é necessário Javascript para funcionar;para quem não o tem habilitado, você sempre pode tornar visível o botão de envio do formulário oculto e instruí-los a pressioná-lo se demorar mais de 5 segundos.Segundo, você pode decidir quais dados serão transmitidos ao servidor de terceiros;se você apenas processar o formulário à medida que ele passa, estará repassando todos os dados da postagem, o que nem sempre é o que você deseja.O mesmo vale para a solução 307, presumindo que funcionou para todos os seus usuários.

Espero que isto ajude!

Outras dicas

Você pode usar esta abordagem:

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 logo após o cliente obter todo o html do servidor do evento carregando ocorre que aciona o envio do formulário e a publicação de todos os dados no postbackUrl definido.

HttpWebRequest é usado para isso.

No postback, crie um HttpWebRequest para terceiros e publique os dados do formulário e, depois de fazer isso, você poderá Response.Redirect onde quiser.

Você obtém a vantagem adicional de não precisar nomear todos os controles do servidor para criar o formulário de terceiros. Você pode fazer essa tradução ao criar a string 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");

No entanto, se você precisar que o usuário veja a página de resposta deste formulário, sua única opção é utilizar Server.Transfer, e isso pode ou não funcionar.

Isso deve tornar a vida muito mais fácil.Você pode simplesmente usar o método Response.RedirectWithData(...) em seu aplicativo da web facilmente.

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 novo no ASP.Net 3.5 é a propriedade "PostBackUrl" dos botões ASP.Você pode configurá-lo para o endereço da página na qual deseja postar diretamente e, quando esse botão for clicado, em vez de postar de volta na mesma página normalmente, ele postará na página que você indicou.Prático.Certifique-se de que UseSubmitBehavior também esteja definido como TRUE.

Achei interessante compartilhar que o heroku faz isso com seu SSO para provedores de complementos

Um exemplo de como funciona pode ser visto na fonte da ferramenta "kensa":

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

E pode ser visto na prática se você desligar o javascript.Fonte da página de exemplo:

<!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 pode ser definido no botão asp para postar em uma página diferente.

se você precisar fazer isso em codebehind, tente Server.Transfer.

@Matt,

Você ainda pode usar o HttpWebRequest e, em seguida, direcionar a resposta recebida para a resposta real do fluxo de saída, isso enviaria a resposta de volta ao usuário.O único problema é que quaisquer URLs relativos seriam quebrados.

Ainda assim, isso pode funcionar.

Aqui está o que eu faria:

Coloque os dados em um formulário padrão (sem o atributo runat="server") e defina a ação do formulário para postar na página externa de destino.Antes de enviar eu enviaria os dados para o meu servidor usando um XmlHttpRequest e analise a resposta.Se a resposta significar que você deve prosseguir com o POST externo, então eu (o JavaScript) prosseguiria com a postagem, caso contrário, redirecionaria para uma página do meu site

Em PHP, você pode enviar dados POST com cURL.Existe algo comparável para .NET?

Sim, HttpWebRequest, veja minha postagem abaixo.

O método GET (e HEAD) nunca deve ser usado para fazer algo que tenha efeitos colaterais.Um efeito colateral pode ser a atualização do estado de um aplicativo da web ou a cobrança em seu cartão de crédito.Se uma ação tiver efeitos colaterais, outro método (POST) deverá ser usado.

Portanto, um usuário (ou seu navegador) não deve ser responsabilizado por algo feito por um GET.Se algum efeito colateral prejudicial ou caro ocorresse como resultado de um GET, isso seria culpa do aplicativo da web, não do usuário.De acordo com as especificações, um agente de usuário Não deve segue automaticamente um redirecionamento, a menos que seja uma resposta a uma solicitação GET ou HEAD.

É claro que muitas solicitações GET têm alguns efeitos colaterais, mesmo que sejam apenas anexadas a um arquivo de log.O importante é que o aplicativo, e não o usuário, seja responsabilizado por esses efeitos.

As seções relevantes da especificação HTTP são 9.1.1 e 9.1.2, e 10.3.

Sugiro construir um HttpWebRequest para executar programaticamente seu POST e depois redirecionar após ler a resposta, se aplicável.

Código copiável e colável com base em Método de Pavlo Neyman

RedirectPost(string url, T bodyPayload) e GetPostData() são para aqueles que desejam apenas despejar alguns dados fortemente digitados na página de origem e recuperá-los na página de destino.Os dados devem ser serializáveis ​​pelo NewtonSoft Json.NET e você precisa fazer referência à biblioteca, é claro.

Basta copiar e colar em sua(s) página(s) ou, melhor ainda, na classe base de suas páginas e usá-la em qualquer lugar de seu aplicativo.

Meu coração está com todos vocês que ainda precisam usar Web Forms em 2019 por qualquer 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, tudo o que você precisa é transportar algum estado entre essas duas solicitações.Na verdade, existe uma maneira muito divertida de fazer isso que não depende de JavaScript (pense em <noscript/>).

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

Com esse cookie lá, você pode na seguinte solicitação para /redirect.html recuperar as informações name=value, você pode armazenar qualquer tipo de informação nesta string de par nome/valor, até dizer 4K de dados (limite típico de cookie).É claro que você deve evitar isso e armazenar códigos de status e bits de sinalização.

Ao receber essa solicitação, você responde com uma solicitação de exclusão desse código de status.

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

Meu HTTP está um pouco enferrujado. Tenho pesquisado RFC2109 e RFC2965 para descobrir o quão confiável isso realmente é, de preferência eu gostaria que o cookie fosse de ida e volta exatamente uma vez, mas isso também não parece ser possível, cookies de terceiros podem ser um problema para você se você estiver mudando para outro domínio.Isso ainda é possível, mas não tão fácil quanto quando você faz coisas em seu próprio domínio.

O problema aqui é a simultaneidade, se um usuário avançado estiver usando várias guias e conseguir intercalar algumas solicitações pertencentes à mesma sessão (isso é muito improvável, mas não impossível), isso pode levar a inconsistências em seu aplicativo.

É a maneira <noscript/> de fazer viagens de ida e volta HTTP sem URLs e JavaScript sem sentido

Eu forneço este código como um exemplo de conceito:Se este código for executado em um contexto com o qual você não está familiarizado, acho que você pode descobrir qual parte é qual.

A ideia é que você chame Relocate com algum estado ao redirecionar, e a URL que você realocou chame GetState para obter os dados (se houver).

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top