Como obter uma resposta ASP.NET MVC Ajax para redirecionar para uma nova página em vez de inserir a exibição em UpdateTargetId?

88

Estou usando o Ajax.BeginForm para criar um formulário que fará um postback de ajax para uma determinada ação do controlador e, se a ação for bem-sucedida, o usuário deve ser redirecionado para outra página (se a ação falhar, uma mensagem de status será exibida usando o AjaxOptions UpdateTargetId).

using (Ajax.BeginForm("Delete", null,
        new { userId = Model.UserId },
        new AjaxOptions { UpdateTargetId = "UserForm", LoadingElementId = "DeletingDiv" },
        new { name = "DeleteForm", id = "DeleteForm" }))
   {
    [HTML DELETE BUTTON]
   }

Se a exclusão for bem-sucedida, estou retornando um resultado de Redirecionamento:

[Authorize]
public ActionResult Delete(Int32 UserId)
{
    UserRepository.DeleteUser(UserId);
    return Redirect(Url.Action("Index", "Home"));
}

Mas a visualização Índice do Home Controller está sendo carregada no UpdateTargetId e, portanto, acabo com uma página dentro de uma página. Duas coisas que estou pensando:

  1. Ou eu estou arquitetando isso de forma errada e devo lidar com esse tipo de ação de forma diferente (sem usar ajax).
  2. Em vez de retornar um resultado de Redirecionamento, retorne um modo de exibição que contém javascript que faz o redirecionamento no lado do cliente.

Alguém tem comentários sobre o nº 1? Ou se o nº 2 for uma boa solução, como seria a "visualização de redirecionamento de javascript"?

Jeff Widmer
fonte

Respostas:

171

Você pode usar JavascriptResultpara conseguir isso.

Para redirecionar:

return JavaScript("window.location = 'http://www.google.co.uk'");

Para recarregar a página atual:

return JavaScript("location.reload(true)");

Parece a opção mais simples.

Ben Foster
fonte
4
Incrível .. Me ajudou.
CrazyCoderz
1
Eu concordo, essa é a abordagem certa. Esta resposta stackoverflow.com/a/2841608/498969 mostra como você pode aplicar essa funcionalidade por padrão usando um controlador de base.
Adam Spicer
@Ben Foster: Usando este navegador tenta abrir o url http :: window.location .. Postei esta como uma pergunta separada em stackoverflow.com/questions/13896307/…
Andrus
30
Se você deseja redirecionar para um controlador em seu projeto, você pode usar o auxiliar de Url para você. Por exemplo:return JavaScript( "window.location = '" + Url.Action("Edit","Dispatch") + "'" );
Chris Morgan
2
Curto. Simples. Excelente. Ótimo!
Vikram
30

Você pode retornar um JSON com o URL e alterar o window.location usando JavaScript no lado do cliente. Prefiro essa maneira do que chamar uma função JavaScript do servidor, o que acho que está quebrando a separação de interesses.

Lado do servidor:

return Json(new {result = "Redirect", url = Url.Action("ActionName", "ControllerName")});

Lado do cliente:

if (response.result == 'Redirect')
    window.location = response.url;

Claro que você pode adicionar mais lógica porque pode haver um erro no lado do servidor e, nesse caso, a propriedade result pode indicar esta situação e evitar o redirecionamento.

Francisco Goldenstein
fonte
12

O comportamento que você está tentando produzir não é realmente melhor executado usando AJAX. AJAX seria melhor usado se você quisesse atualizar apenas uma parte da página, não redirecionar completamente para alguma outra página. Isso realmente derrota todo o propósito do AJAX.

Eu sugeriria apenas não usar AJAX com o comportamento que você está descrevendo.

Como alternativa, você pode tentar usar jquery Ajax, que enviará a solicitação e, em seguida, especifique um retorno de chamada quando a solicitação for concluída. No retorno de chamada, você pode determinar se ele falhou ou teve êxito e redirecionar para outra página em caso de sucesso. Descobri que o jquery Ajax é muito mais fácil de usar, especialmente porque já estou usando a biblioteca para outras coisas.

Você pode encontrar a documentação sobre jquery ajax aqui , mas a sintaxe é a seguinte:

jQuery.ajax( options )  

jQuery.get( url, data, callback, type)

jQuery.getJSON( url, data, callback )

jQuery.getScript( url, callback )

jQuery.post( url, data, callback, type)
Joseph
fonte
Então, basta fazer um POST normal para a ação Excluir e, na maioria dos casos, ele terá êxito e, então, posso redirecionar para a página de índice inicial. Nos casos em que houver falha, exiba uma visualização diferente com as mensagens de erro e a ação apropriada para o usuário executar. Parece bom?
Jeff Widmer,
Sim, parece que faria mais sentido.
Joseph,
Fui em frente com a conversão de um envio Ajax para um POST regular, de acordo com a recomendação de Joseph. Na verdade, isso funcionou melhor, pois me deu uma maneira mais limpa de lidar com as mensagens de confirmação de exclusão.
Jeff Widmer,
12

Embora não seja elegante, funciona para mim em certas situações.

Controlador

if (RedirectToPage)
    return PartialView("JavascriptRedirect", new JavascriptRedirectModel("http://www.google.com"));
else
   ... return regular ajax partialview

Modelo

    public JavascriptRedirectModel(string location)
    {
        Location = location;
    }

    public string Location { get; set; }

/Views/Shared/JavascriptRedirect.cshtml

@model Models.Shared.JavascriptRedirectModel

<script type="text/javascript">
    window.location = '@Model.Location';
</script>
Valamas
fonte
2
Por que não elegante? É fortemente tipado e parece claro. Eu gosto dessa abordagem. +1
T-moty
6

Usar JavaScriptdefinitivamente fará o trabalho.

Você também pode usar Contentse este for mais o seu estilo.

Exemplo:

Controlador MVC

[HttpPost]
public ActionResult AjaxMethod()
{
    return Content(@"http://www.google.co.uk");
}

Javascript

$.ajax({
    type: 'POST',
    url: '/AjaxMethod',
    success: function (redirect) {
        window.location = redirect;
    }
});
christo8989
fonte
Obrigado! Isso realmente me ajudou.
dee
5

Você pode simplesmente escrever em Ajax Success como abaixo:

 $.ajax({
            type: "POST",
            url: '@Url.Action("GetUserList", "User")',
            data: { id: $("#UID").val() },
            success: function (data) {
                window.location.href = '@Url.Action("Dashboard", "User")';
            },
            error: function () {
                $("#loader").fadeOut("slow");
            }
});
Hiren Patel
fonte
2

Que tal agora :

public ActionResult GetGrid()
{
   string url = "login.html";
   return new HttpStatusCodeResult(System.Net.HttpStatusCode.Redirect,url)
}

E depois

$(document).ajaxError(function (event, jqxhr, settings, thrownError) { 
   if (jqxhr.status == 302) {
      location.href = jqxhr.statusText;
   }           
});

Ou

error: function (a, b, c) {
       if (a.status == 302) {
         location.href = a.statusText;
       }  
}
Bimal Das
fonte
1

Eu precisava fazer isso porque tenho um formulário de login ajax. Quando os usuários fazem o login com sucesso, redireciono para uma nova página e termino a solicitação anterior porque a outra página trata do redirecionamento de volta para a parte confiável (porque é um sistema SSO STS).

No entanto, eu também queria que funcionasse com o javascript desabilitado, sendo o salto central de login e tudo, então eu vim com isso,

    public static string EnsureUrlEndsWithSlash(string url)
    {
        if (string.IsNullOrEmpty(url))
            throw new ArgumentNullException("url");
        if (!url.EndsWith("/"))
            return string.Concat(url, "/");
        return url;
    }

    public static string GetQueryStringFromArray(KeyValuePair<string, string>[] values)
    {
        Dictionary<string, string> dValues = new Dictionary<string,string>();
        foreach(var pair in values)            
            dValues.Add(pair.Key, pair.Value);            
        var array = (from key in dValues.Keys select string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(dValues[key]))).ToArray();
        return "?" + string.Join("&", array);
    }

    public static void RedirectTo(this HttpRequestBase request, string url, params KeyValuePair<string, string>[] queryParameters)
    {            
        string redirectUrl = string.Concat(EnsureUrlEndsWithSlash(url), GetQueryStringFromArray(queryParameters));
        if (request.IsAjaxRequest())
            HttpContext.Current.Response.Write(string.Format("<script type=\"text/javascript\">window.location='{0}';</script>", redirectUrl));
        else
            HttpContext.Current.Response.Redirect(redirectUrl, true);

    }
Ryan Mann
fonte
1

Você pode simplesmente fazer algum tipo de filtro de resposta ajax para respostas de entrada com $ .ajaxSetup . Se a resposta contém redirecionamento MVC, você pode avaliar esta expressão no lado JS. Exemplo de código para JS abaixo:

$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

Se os dados forem: "window.location = '/ Acount / Login'", o filtro acima irá capturar isso e avaliar para fazer o redirecionamento.

Przemek Marcinkiewicz
fonte
0

Não estou satisfeito com a melhor resposta do Joseph, ao invés de corrigir o problema correto, ele disse que este é o caso de uso errado. Na verdade, existem muitos lugares, por exemplo, se você está convertendo uma base de código antiga para um código ajaxificado e aí você PRECISA, então você PRECISA. Na programação não há desculpa porque não é só você que está codificando, são todos desenvolvedores bons e ruins e você tem que trabalhar lado a lado. Portanto, se eu não codificar o redirecionamento no ajax, meu colega desenvolvedor pode me forçar a encontrar uma solução para ele. Assim como gosto de usar todos os sites padronizados da AMD ou mvc4, e minha empresa pode me manter afastado dele por um ano.

Então, vamos falar sobre a solução agora.

Eu fiz o inferno com o tratamento de solicitações e respostas ajax e a maneira mais simples que descobri foi enviar códigos de status para o cliente e ter uma função javascript padrão para entender esses códigos. Se eu simplesmente enviar, por exemplo, o código 13, isso pode significar um redirecionamento.

Portanto, uma resposta json como {statusCode: 13, messsage: '/ home / logado'} é claro que há toneladas de variações propostas como {status: 'sucesso', código: 13, url: '/ home / logado ', mensagem:' Você está logado agora '}

etc, então você escolhe as mensagens padrão

Normalmente eu herdo da classe base do controlador e coloco minha escolha de respostas padrão como esta

public JsonResult JsonDataResult(object data, string optionalMessage = "")
    {
        return Json(new { data = data, status = "success", message = optionalMessage }, JsonRequestBehavior.AllowGet);
    }

    public JsonResult JsonSuccessResult(string message)
    {
        return Json(new { data = "", status = "success", message = message }, JsonRequestBehavior.AllowGet);
    }

    public JsonResult JsonErrorResult(string message)
    {
        return Json(new { data = "", status = "error", message = message }, JsonRequestBehavior.AllowGet);
    }

    public JsonResult JsonRawResult(object data)
    {
        return Json(data, JsonRequestBehavior.AllowGet);
    }

Sobre usar $ .ajax em vez de Ajax.BeginForm Eu adoraria usar Jquery ajax e eu uso, mas novamente não sou eu em todo o mundo para tomar decisões. Eu tenho um aplicativo cheio de Ajax.BeginForm e é claro que não fiz isso. Mas eu tenho que viver com isso.

Portanto, há um retorno de chamada de sucesso no formulário inicial também, você não precisa usar jquery ajax para usar retornos de chamada. Algo sobre isso aqui Ajax.BeginForm, Chama ação, Retorna JSON, Como faço para acessar o objeto JSON em minha função JS OnSuccess?

obrigado

Asif Ashraf
fonte
0

Se você estiver redirecionando da classe JavaScript

mesma visão - controlador diferente

<strike>window.location.href = `'Home'`;</strike>

não é a mesma visão

<strike>window.location.href = `'Index/Home'`;</strike>
Aslanpayi
fonte
0

Adicione uma classe auxiliar:

public static class Redirector {
        public static void RedirectTo(this Controller ct, string action) {
            UrlHelper urlHelper = new UrlHelper(ct.ControllerContext.RequestContext);

            ct.Response.Headers.Add("AjaxRedirectURL", urlHelper.Action(action));
        }

        public static void RedirectTo(this Controller ct, string action, string controller) {
            UrlHelper urlHelper = new UrlHelper(ct.ControllerContext.RequestContext);

            ct.Response.Headers.Add("AjaxRedirectURL", urlHelper.Action(action, controller));
        }

        public static void RedirectTo(this Controller ct, string action, string controller, object routeValues) {
            UrlHelper urlHelper = new UrlHelper(ct.ControllerContext.RequestContext);

            ct.Response.Headers.Add("AjaxRedirectURL", urlHelper.Action(action, controller, routeValues));
        }
    }

Em seguida, peça sua ação:

this.RedirectTo ("Índice", "Cimento");

Adicione o código javascript a qualquer arquivo javascript global incluído ou arquivo de layout para interceptar todas as solicitações ajax:

<script type="text/javascript">
  $(function() {
    $(document).ajaxComplete(function (event, xhr, settings) {
            var urlHeader = xhr.getResponseHeader('AjaxRedirectURL');

            if (urlHeader != null && urlHeader !== undefined) {
                window.location = xhr.getResponseHeader('AjaxRedirectURL');
            }
        });
  });
</script>

Faisale
fonte
0

Como diz ben foster, você pode retornar os Javascripts e isso o redirecionará para a página desejada.

Para carregar a página na página atual:

return JavaScript("window.location = 'http://www.google.co.uk'");'

Para carregar a página na nova guia:

return JavaScript("window.open('http://www.google.co.uk')");
Trilok Pathak
fonte
0

Você pode obter um redirecionamento não baseado em JS de uma chamada ajax, colocando uma dessas meta tags de atualização. Isso aqui parece estar funcionando: return Content("<meta http-equiv=\"refresh\" content=\"0;URL='" + @Url.Action("Index", "Home") + "'\" />");

Nota: descobri que as meta-atualizações são desabilitadas automaticamente pelo Firefox, tornando isso não muito útil.

Vasily Hall
fonte