Jak uzyskać odpowiedź ASP.NET MVC Ajax w celu przekierowania do nowej strony zamiast wstawiania widoku do UpdateTargetId?

88

Używam Ajax.BeginForm do stworzenia formularza, który wykona postback w Ajax do określonej akcji kontrolera, a następnie, jeśli akcja się powiedzie, użytkownik powinien zostać przekierowany na inną stronę (jeśli akcja się nie powiedzie, zostanie wyświetlony komunikat o stanie za pomocą 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]
   }

Jeśli usunięcie powiedzie się, zwracam wynik przekierowania:

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

Ale widok indeksu kontrolera głównego jest ładowany do UpdateTargetId i dlatego kończy się strona na stronie. Dwie rzeczy, o których myślę:

  1. Albo projektuję to źle i powinienem obsługiwać ten typ akcji inaczej (nie używając Ajax).
  2. Zamiast zwracać wynik przekierowania, zwróć widok, który zawiera javascript, który wykonuje przekierowanie po stronie klienta.

Czy ktoś ma komentarze do nr 1? A jeśli punkt 2 jest dobrym rozwiązaniem, jak wyglądałby „przekierowany widok javascript”?

Jeff Widmer
źródło

Odpowiedzi:

171

Możesz użyć, JavascriptResultaby to osiągnąć.

Aby przekierować:

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

Aby ponownie załadować bieżącą stronę:

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

Wydaje się najprostszą opcją.

Ben Foster
źródło
4
Niesamowite ... Pomogło mi.
CrazyCoderz
1
Zgadzam się, to jest właściwe podejście. Ta odpowiedź stackoverflow.com/a/2841608/498969 pokazuje, jak można zastosować tę funkcję domyślnie przy użyciu kontrolera podstawowego.
Adam Spicer
@Ben Foster: Korzystając z tej przeglądarki, próbuje otworzyć adres URL http :: window.location ... Opublikowałem to jako osobne pytanie na stackoverflow.com/questions/13896307/ ...
Andrus
30
Jeśli chcesz przekierować do kontrolera w swoim projekcie, możesz użyć pomocnika adresu URL. Na przykład:return JavaScript( "window.location = '" + Url.Action("Edit","Dispatch") + "'" );
Chris Morgan
2
Krótki. Prosty. Świetny. Świetny!
Vikram
30

Możesz zwrócić JSON z adresem URL i zmienić window.location za pomocą JavaScript po stronie klienta. Wolę ten sposób niż wywoływanie funkcji JavaScript z serwera, co wydaje mi się, że przerywa podział obaw.

Po stronie serwera:

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

Strona klienta:

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

Oczywiście możesz dodać więcej logiki, ponieważ może wystąpić błąd po stronie serwera iw takim przypadku właściwość result może wskazywać na taką sytuację i uniknąć przekierowania.

Francisco Goldenstein
źródło
12

Zachowanie, które próbujesz uzyskać, nie jest najlepiej wykonywane przy użyciu technologii AJAX. AJAX byłby najlepszy, gdybyś chciał zaktualizować tylko część strony, a nie całkowicie przekierować na inną stronę. To naprawdę podważa cały cel AJAX.

Sugerowałbym, aby po prostu nie używać AJAX z zachowaniem, które opisujesz.

Alternatywnie możesz spróbować użyć jquery Ajax, który przesyła żądanie, a następnie określa wywołanie zwrotne po zakończeniu żądania. W wywołaniu zwrotnym możesz określić, czy się nie powiodło, czy powiodło się, a po pomyślnym przekierowaniu na inną stronę. Zauważyłem, że jquery Ajax jest dużo łatwiejszy w użyciu, zwłaszcza że i tak używam biblioteki do innych celów.

Możesz znaleźć dokumentację na temat jquery ajax tutaj , ale składnia jest następująca:

jQuery.ajax( options )  

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

jQuery.getJSON( url, data, callback )

jQuery.getScript( url, callback )

jQuery.post( url, data, callback, type)
Józef
źródło
Następnie po prostu wykonaj zwykły POST do akcji Usuń iw większości przypadków się powiedzie, a następnie mogę przekierować do strony indeksu głównego. W przypadkach, w których to się nie powiedzie, wyświetl inny widok z komunikatami o błędach i odpowiednią czynnością, którą powinien podjąć użytkownik. Brzmi dobrze?
Jeff Widmer
Tak, to brzmi, jakby to miało więcej sensu.
Joseph
Poszedłem do przodu i przekonwertowałem to z przesłania Ajax do zwykłego POST zgodnie z zaleceniem Josepha. To faktycznie zadziałało lepiej, ponieważ dało mi bardziej przejrzysty sposób obsługi komunikatów potwierdzających usunięcie.
Jeff Widmer
12

Chociaż nie jest elegancki, działa na mnie w pewnych sytuacjach.

Kontroler

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

Model

    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
źródło
2
Dlaczego nie eleganckie? Jest mocno wpisany na maszynie i wydaje się jasny. Podoba mi się to podejście. +1
T-moty
6

Używanie na JavaScriptpewno załatwi sprawę.

Możesz również użyć, Contentjeśli to bardziej Twój styl.

Przykład:

Kontroler 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
źródło
Dziękuję Ci! To naprawdę mi pomogło.
dee
5

Możesz po prostu napisać w Ajax Success, jak poniżej:

 $.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
źródło
2

Co powiesz na to :

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

I wtedy

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

Lub

error: function (a, b, c) {
       if (a.status == 302) {
         location.href = a.statusText;
       }  
}
Bimal Das
źródło
1

Musiałem to zrobić, ponieważ mam formularz logowania Ajax. Gdy użytkownicy logują się pomyślnie, przekierowuję do nowej strony i kończę poprzednie żądanie, ponieważ druga strona obsługuje przekierowanie z powrotem do strony uzależnionej (ponieważ jest to system logowania jednokrotnego STS).

Jednak chciałem również, aby działał z wyłączoną obsługą javascript, będąc centralnym przeskokiem logowania i wszystkim innym, więc wymyśliłem to,

    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
źródło
1

Możesz po prostu zrobić jakiś rodzaj filtru odpowiedzi Ajax dla odpowiedzi przychodzących za pomocą $ .ajaxSetup . Jeśli odpowiedź zawiera przekierowanie MVC, możesz ocenić to wyrażenie po stronie JS. Przykładowy kod JS poniżej:

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

Jeśli dane to: „window.location = '/ Acount / Login'” powyższy filtr wykryje to i oszacuje, aby wykonać przekierowanie.

Przemek Marcinkiewicz
źródło
0

Nie jestem usatysfakcjonowany najlepszą odpowiedzią Josepha, zamiast naprawić właściwy problem, powiedział, że jest to zły przypadek użycia. W rzeczywistości jest wiele miejsc, na przykład jeśli konwertujesz stary kod do kodu Ajax i tam go POTRZEBUJESZ, to POTRZEBUJESZ go. W programowaniu nie ma wymówki, ponieważ nie tylko Ty kodujesz wszystkich złych i dobrych programistów i musisz pracować ramię w ramię. Więc jeśli nie przekierowuję kodu w Ajax, mój kolega programista może zmusić mnie do znalezienia rozwiązania. Tak jak lubię korzystać ze wszystkich wzorowanych witryn AMD lub mvc4, a moja firma może trzymać mnie z daleka od tego przez rok.

Porozmawiajmy teraz o rozwiązaniu.

Zrobiłem do diabła z obsługą żądań i odpowiedzi Ajaxa, a najprostszym sposobem, jaki znalazłem, było wysłanie kodów stanu do klienta i posiadanie jednej standardowej funkcji javascript do zrozumienia tych kodów. Jeśli po prostu wyślę na przykład kod 13, może to oznaczać przekierowanie.

Więc odpowiedź json, taka jak {statusCode: 13, messsage: '/ home / logged-in'}, oczywiście jest mnóstwo proponowanych wariantów, takich jak {status: 'success', code: 13, url: '/ home / logged-in ', komunikat:' Jesteś teraz zalogowany '}

itd., więc do własnego wyboru standardowych wiadomości

Zwykle dziedziczę z podstawowej klasy kontrolera i umieszczam wybór standardowych odpowiedzi, takich jak ta

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

O używaniu $ .ajax intead z Ajax.BeginForm Bardzo chciałbym używać Jquery ajax i robię to, ale znowu to nie ja na całym świecie do podejmowania decyzji Mam aplikację pełną Ajax.BeginForm i oczywiście tego nie zrobiłem. Ale muszę z tym żyć.

Więc jest również pomyślne wywołanie zwrotne w formie początkowej, nie musisz używać jquery ajax, aby używać wywołań zwrotnych. Coś o tym tutaj Ajax.BeginForm, wzywa akcję, zwraca JSON, Jak uzyskać dostęp do obiektu JSON w mojej funkcji OnSuccess JS?

Dzięki

Asif Ashraf
źródło
0

Jeśli jesteś przekierowany z klasy JavaScript

ten sam widok - inny kontroler

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

to nie ten sam widok

<strike>window.location.href = `'Index/Home'`;</strike>
aslanpayi
źródło
0

Dodaj klasę pomocniczą:

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

Następnie wezwij do działania:

this.RedirectTo ("Indeks", "Cement");

Dodaj kod javascript do dowolnego globalnego pliku dołączonego do javascript lub pliku układu, aby przechwycić wszystkie żądania 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
źródło
0

Jak mówi Ben Foster, możesz zwrócić JavaScript, a to przekieruje Cię na żądaną stronę.

Aby załadować stronę na bieżącej stronie:

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

Aby załadować stronę w nowej karcie:

return JavaScript("window.open('http://www.google.co.uk')");
Trilok Pathak
źródło
0

Możesz uzyskać przekierowanie nie oparte na js z wywołania Ajax, wstawiając jeden z tych metatagów odświeżania. To wydaje się działać: return Content("<meta http-equiv=\"refresh\" content=\"0;URL='" + @Url.Action("Index", "Home") + "'\" />");

Uwaga: odkryłem, że odświeżanie meta jest automatycznie wyłączane przez przeglądarkę Firefox, przez co nie jest to zbyt przydatne.

Wasilij Hall
źródło