How to get an ASP.NET MVC Ajax response to redirect to new page instead of inserting view into UpdateTargetId?

C#Ajaxasp.net Mvcasp.net Mvc-Ajax

C# Problem Overview


I am using the Ajax.BeginForm to create a form the will do an ajax postback to a certain controller action and then if the action is successful, the user should get redirected to another page (if the action fails then a status message gets displayed using the 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]
   }

If the delete is successful I am returning a Redirect result:

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

But the Home Controller Index view is getting loaded into the UpdateTargetId and therefore I end up with a page within a page. Two things I am thinking about:

  1. Either I am architecting this wrong and should handle this type of action differently (not using ajax).
  2. Instead of returning a Redirect result, return a view which has javascript in it that does the redirect on the client side.

Does anyone have comments on #1? Or if #2 is a good solution, what would the "redirect javascript view" look like?

C# Solutions


Solution 1 - C#

You can use JavascriptResult to achieve this.

To redirect:

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

To reload the current page:

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

Seems the simplest option.

Solution 2 - C#

You can return a JSON with the URL and change the window.location using JavaScript at client side. I prefer this way than calling a JavaScript function from the server, which I think that it's breaking the separation of concerns.

Server side:

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

Client side:

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

Of course you can add more logic because there could be an error on the server side and in that case the result property could indicate this situation and avoid the redirection.

Solution 3 - C#

While not elegant, works for me in certain situations.

Controller

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>

Solution 4 - C#

The behavior you're trying to produce is not really best done using AJAX. AJAX would be best used if you wanted to only update a portion of the page, not completely redirect to some other page. That defeats the whole purpose of AJAX really.

I would suggest to just not use AJAX with the behavior you're describing.

Alternatively, you could try using jquery Ajax, which would submit the request and then you specify a callback when the request completes. In the callback you could determine if it failed or succeeded, and redirect to another page on success. I've found jquery Ajax to be much easier to use, especially since I'm already using the library for other things anyway.

You can find documentation about jquery ajax here, but the syntax is as follows:

jQuery.ajax( options )	

jQuery.get( url, data, callback, type)
 
jQuery.getJSON( url, data, callback )
 
jQuery.getScript( url, callback )
 
jQuery.post( url, data, callback, type)

Solution 5 - C#

Using JavaScript will definitely do the job.

You can also use Content if this is more your style.

Example:

MVC Controller

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

Javascript

$.ajax({
    type: 'POST',
    url: '/AjaxMethod',
    success: function (redirect) {
        window.location = redirect;
    }
});

Solution 6 - C#

You can simply write in Ajax Success like below :

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

Solution 7 - C#

How about this :

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

And then

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

Or

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

Solution 8 - C#

I m not satisfied by the best answer by the Joseph, instead of fixing the correct problem, he told that this is wrong use case. In fact there are many places for example if you are converting an old codebase to ajaxified code and there you NEED it, then you NEED it. In programming there is no excuse because its not only you who is coding its all bad and good developers and you have to work side by side. So if I don't code redirection in ajax my fellow developer can force me to have a solution for it. Just like I like to use all AMD patterned sites or mvc4, and my company can keep me away from it for a year.

So let's talk on the solution now.

I have done hell heck of ajax request and response handling and the simplest way I found out was to send status codes to the client and have one standard javascript function to understand those codes. If i simply send for example code 13 it might meant a redirect.

So a json response like { statusCode: 13, messsage: '/home/logged-in' } of course there are tons of variations proposed like { status: 'success', code: 13, url: '/home/logged-in', message: 'You are logged in now' }

etc , so up to your own choice of standard messages

Usually I Inherit from base Controller class and put my choice of standard responses like this

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

About using $.ajax intead of Ajax.BeginForm I would love to use Jquery ajax and I do, but again its not me in the whole world to make decisions I have an application full of Ajax.BeginForm and of course I didnt do that. But i have to live with it.

So There is a success callback in begin form too, you don't need to use jquery ajax to use callbacks Something about it here https://stackoverflow.com/questions/4020705/ajax-beginform-calls-action-returns-json-how-do-i-access-json-object-in-my-on

Thanks

Solution 9 - C#

I needed to do this because I have an ajax login form. When users login successfully I redirect to a new page and end the previous request because the other page handles redirecting back to the relying party (because it's a STS SSO System).

However, I also wanted it to work with javascript disabled, being the central login hop and all, so I came up with this,

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

Solution 10 - C#

You can simply do some kind of ajax response filter for incomming responses with $.ajaxSetup. If the response contains MVC redirection you can evaluate this expression on JS side. Example code for JS below:

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

If data is: "window.location = '/Acount/Login'" above filter will catch that and evaluate to make the redirection.

Solution 11 - C#

If you're redirect from the JavaScript class

same view - diferent controller

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

is not same view

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

Solution 12 - C#

Add a helper class:

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

Then call in your action:

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

Add javascript code to any global javascript included file or layout file to intercept all ajax requests:

Solution 13 - C#

As ben foster says you can return the Javascripts and it will redirect you to the desired page.

To load page in the current page:

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

To load page in the new tab:

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

Solution 14 - C#

You can get a non-js-based redirection from an ajax call by putting in one of those meta refresh tags. This here seems to be working:

return Content("<meta http-equiv=\"refresh\" content=\"0;URL='" + @Url.Action("Index", "Home") + "'\" />");```

Note: I discovered that meta refreshes are auto-disabled by Firefox, rendering this not very useful.

Solution 15 - C#

The accepted answer works well except for the fact that the javascript is briefly displayed in whatever the ajax target element is. To get around this, create a partial view called _Redirect with the following code:

@model string
<script>
   window.location = '@Model';
</script>

Then, in the controller replace

return Redirect(Url.Action("Index", "Home"));

with

return PartialView("_Redirect",Url.Action("Index", "Home"));

The effect is the same as the accepted answer, but without the brief artifact in the display. Place the _Redirect.cshtml in the shared folder so it can be used from anywhere.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJeff WidmerView Question on Stackoverflow
Solution 1 - C#Ben FosterView Answer on Stackoverflow
Solution 2 - C#Francisco GoldensteinView Answer on Stackoverflow
Solution 3 - C#ValamasView Answer on Stackoverflow
Solution 4 - C#JosephView Answer on Stackoverflow
Solution 5 - C#christo8989View Answer on Stackoverflow
Solution 6 - C#Hiren PatelView Answer on Stackoverflow
Solution 7 - C#Bimal DasView Answer on Stackoverflow
Solution 8 - C#Asif AshrafView Answer on Stackoverflow
Solution 9 - C#Ryan MannView Answer on Stackoverflow
Solution 10 - C#Przemek MarcinkiewiczView Answer on Stackoverflow
Solution 11 - C#aslanpayiView Answer on Stackoverflow
Solution 12 - C#faisaleView Answer on Stackoverflow
Solution 13 - C#Trilok PathakView Answer on Stackoverflow
Solution 14 - C#Vasily HallView Answer on Stackoverflow
Solution 15 - C#Gene StempelView Answer on Stackoverflow