¿Cómo obtener una respuesta ASP.NET MVC Ajax para redirigir a una nueva página en lugar de insertar una vista en UpdateTargetId?

88

Estoy usando Ajax.BeginForm para crear un formulario que hará una devolución de datos ajax a una determinada acción del controlador y luego, si la acción es exitosa, el usuario debe ser redirigido a otra página (si la acción falla, se muestra un mensaje de estado usando el UpdateTargetId de AjaxOptions).

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

Si la eliminación es exitosa, estoy devolviendo un resultado de redireccionamiento:

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

Pero la vista del índice del controlador de inicio se está cargando en UpdateTargetId y, por lo tanto, termino con una página dentro de una página. Dos cosas en las que estoy pensando:

  1. O estoy diseñando esto mal y debería manejar este tipo de acción de manera diferente (sin usar ajax).
  2. En lugar de devolver un resultado de redireccionamiento, devuelva una vista que contenga javascript que realiza el redireccionamiento en el lado del cliente.

¿Alguien tiene comentarios sobre el n. ° 1? O si el n. ° 2 es una buena solución, ¿cómo se vería la "vista de redirección de JavaScript"?

Jeff Widmer
fuente

Respuestas:

171

Puede utilizar JavascriptResultpara lograr esto.

Para redirigir:

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

Para recargar la página actual:

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

Parece la opción más sencilla.

Ben Foster
fuente
4
Impresionante .. Me ayudó.
CrazyCoderz
1
Estoy de acuerdo, este es el enfoque correcto. Esta respuesta stackoverflow.com/a/2841608/498969 muestra cómo puede aplicar esta funcionalidad de forma predeterminada utilizando un controlador base.
Adam Spicer
@Ben Foster: Con este navegador se intenta abrir la URL http :: window.location ... Publiqué esto como una pregunta separada en stackoverflow.com/questions/13896307/…
Andrus
30
Si desea redirigir a un controlador en su proyecto, puede usar el asistente de URL por usted. Por ejemplo:return JavaScript( "window.location = '" + Url.Action("Edit","Dispatch") + "'" );
Chris Morgan
2
Corto. Sencillo. Excelente. ¡Excelente!
Vikram
30

Puede devolver un JSON con la URL y cambiar la ubicación de la ventana usando JavaScript en el lado del cliente. Prefiero esta forma que llamar a una función de JavaScript desde el servidor, que creo que está rompiendo la separación de preocupaciones.

Lado del servidor:

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

Lado del cliente:

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

Por supuesto, puede agregar más lógica porque podría haber un error en el lado del servidor y, en ese caso, la propiedad del resultado podría indicar esta situación y evitar la redirección.

Francisco Goldenstein
fuente
12

El comportamiento que está intentando producir no se hace realmente mejor con AJAX. AJAX se utilizaría mejor si solo desea actualizar una parte de la página, no redirigir completamente a otra página. Eso frustra todo el propósito de AJAX realmente.

Sugeriría simplemente no usar AJAX con el comportamiento que está describiendo.

Alternativamente, puede intentar usar jquery Ajax, que enviaría la solicitud y luego especificaría una devolución de llamada cuando se complete la solicitud. En la devolución de llamada, puede determinar si falló o tuvo éxito, y redirigir a otra página en caso de éxito. He descubierto que jquery Ajax es mucho más fácil de usar, especialmente porque ya estoy usando la biblioteca para otras cosas de todos modos.

Puede encontrar documentación sobre jquery ajax aquí , pero la sintaxis es la siguiente:

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
fuente
Entonces, simplemente haga un POST regular para la acción Eliminar y, en la mayoría de los casos, tendrá éxito y luego puedo redirigir a la página de índice de inicio. En los casos en los que fallara, muestre una vista diferente con los mensajes de error y la acción apropiada que debe realizar el usuario. ¿Suena bien?
Jeff Widmer
Sí, eso parece que tendría más sentido.
Joseph
Seguí adelante con la conversión de esto de un envío Ajax a un POST regular según la recomendación de Joseph. En realidad, esto funcionó mejor ya que me dio una forma más limpia de manejar los mensajes de confirmación de eliminación.
Jeff Widmer
12

Aunque no es elegante, me funciona en determinadas situaciones.

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
fuente
2
¿Por qué no elegante? Está fuertemente tipado y se siente claro. Me gusta ese enfoque. +1
T-moty
6

Usar JavaScriptdefinitivamente hará el trabajo.

También puedes usar Contentsi este es más tu estilo.

Ejemplo:

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
fuente
¡Gracias! Esto realmente me ayudó.
dee
5

Simplemente puede escribir en Ajax Success como se muestra a continuación:

 $.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
fuente
2

Qué tal esto :

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

Y entonces

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

O

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

Necesitaba hacer esto porque tengo un formulario de inicio de sesión ajax. Cuando los usuarios inician sesión correctamente, redirijo a una nueva página y finalizo la solicitud anterior porque la otra página maneja la redirección de regreso a la parte de confianza (porque es un sistema SSO de STS).

Sin embargo, también quería que funcionara con javascript desactivado, siendo el salto de inicio de sesión central y todo, así que se me ocurrió esto,

    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
fuente
1

Simplemente puede hacer algún tipo de filtro de respuesta ajax para las respuestas entrantes con $ .ajaxSetup . Si la respuesta contiene la redirección MVC, puede evaluar esta expresión en el lado de JS. Código de ejemplo para JS a continuación:

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

Si los datos son: "window.location = '/ Acount / Login'", el filtro anterior lo detectará y evaluará para realizar la redirección.

Przemek Marcinkiewicz
fuente
0

No estoy satisfecho con la mejor respuesta de Joseph, en lugar de solucionar el problema correcto, dijo que este es un caso de uso incorrecto. De hecho, hay muchos lugares, por ejemplo, si está convirtiendo una base de código antigua a un código ajaxificado y allí lo NECESITA, entonces lo NECESITA. En programación no hay excusa porque no solo tú estás codificando, todos son buenos y malos desarrolladores y tienes que trabajar codo con codo. Entonces, si no codifico la redirección en ajax, mi compañero desarrollador puede obligarme a tener una solución para ello. Al igual que me gusta usar todos los sitios con patrones de AMD o mvc4, y mi empresa puede mantenerme alejado de ellos durante un año.

Así que hablemos de la solución ahora.

He hecho un infierno de manejo de solicitudes y respuestas ajax y la forma más sencilla que descubrí fue enviar códigos de estado al cliente y tener una función javascript estándar para comprender esos códigos. Si simplemente envío, por ejemplo, el código 13, podría significar una redirección.

Entonces, una respuesta json como {statusCode: 13, message: '/ home / log-in'} por supuesto que hay toneladas de variaciones propuestas como {status: 'success', code: 13, url: '/ home / log-in ', mensaje:' Ha iniciado sesión ahora '}

etc, por lo que depende de su propia elección de mensajes estándar

Por lo general, heredo de la clase base Controller y pongo mi elección de respuestas estándar 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);
    }

Acerca del uso de $ .ajax en lugar de Ajax.BeginForm Me encantaría usar Jquery ajax y lo hago, pero nuevamente no soy yo en todo el mundo para tomar decisiones.Tengo una aplicación llena de Ajax.BeginForm y, por supuesto, no hice eso. Pero tengo que vivir con eso.

Por lo tanto, también hay una devolución de llamada exitosa en el formulario de inicio, no necesita usar jquery ajax para usar devoluciones de llamada .

Gracias

Asif Ashraf
fuente
0

Si es redirigido desde la clase JavaScript

misma vista - controlador diferente

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

no es la misma vista

<strike>window.location.href = `'Index/Home'`;</strike>
aslanpayi
fuente
0

Agrega una clase de ayuda:

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

Luego llame a su acción:

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

Agregue código javascript a cualquier archivo global incluido en javascript o archivo de diseño para interceptar todas las solicitudes 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
fuente
0

Como dice ben foster, puede devolver los javascripts y lo redirigirá a la página deseada.

Para cargar la página en la página actual:

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

Para cargar la página en la nueva pestaña:

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

Puede obtener una redirección no basada en js desde una llamada ajax colocando una de esas metaetiquetas de actualización. Esto aquí parece estar funcionando: return Content("<meta http-equiv=\"refresh\" content=\"0;URL='" + @Url.Action("Index", "Home") + "'\" />");

Nota: descubrí que Firefox desactiva automáticamente las meta actualizaciones, lo que hace que esto no sea muy útil.

Vasily Hall
fuente