如何让ASP.NET MVC Ajax响应重定向到新页面而不是将视图插入UpdateTargetId?

96

我正在使用Ajax.BeginForm创建一个表单,该表单将执行一个ajax回发到特定的控制器操作,然后如果操作成功,用户应该被重定向到另一页(如果操作失败,则使用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]
   }
如果删除成功,我将返回一个重定向结果:
[Authorize]
public ActionResult Delete(Int32 UserId)
{
    UserRepository.DeleteUser(UserId);
    return Redirect(Url.Action("Index", "Home"));
}

然而,主页控制器索引视图被加载到UpdateTargetId中,因此我最终得到了一个页面内的页面。 我正在考虑两件事:

  1. 要么我设计得不对,应该以不同的方式处理这种类型的操作(不使用ajax)。
  2. 不返回Redirect结果,而是返回一个带有javascript的视图,在客户端上执行重定向。

有谁对#1有评论? 如果#2是一个好的解决方案,"redirect javascript view"会是什么样子?

15个回答

175
你可以使用JavascriptResult来实现这个功能。
要进行重定向:
return JavaScript("window.location = 'http://www.google.co.uk'");
重新加载当前页面:

要重新加载当前页面:

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

看起来这是最简单的选项。


1
我同意,这是正确的方法。这个答案http://stackoverflow.com/a/2841608/498969展示了如何通过使用基本控制器默认应用此功能。 - Adam Spicer
@Ben Foster:使用此浏览器尝试打开URL http::window.location...。我将其作为单独的问题发布在http://stackoverflow.com/questions/13896307/how-to-fix-child-actions-are-not-allowed-to-perform-redirect-actions-other-answ中。 - Andrus
30
如果你想在项目中重定向到一个控制器,可以使用 Url 辅助程序来实现。例如:return JavaScript( "window.location = '" + Url.Action("Edit","Dispatch") + "'" ); - Chris Morgan
这让我想哭。思路的组合和基本控制器的概念非常棒。 - secretwep

36

你可以使用JavaScript在客户端返回一个带有URL的JSON并且更改window.location。我更喜欢这种方式而不是从服务器调用JavaScript函数,因为我认为那会破坏关注点分离。

服务器端:

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

客户端:

if (response.result == 'Redirect')
    window.location = response.url;
当然可以添加更多的逻辑,因为服务器端可能会出现错误,在这种情况下,结果属性可能会指示此情况并避免重定向。

服务器端的操作类型是HttpPost吗? - Balasubramanian Ramamoorthi

15

虽然不够优雅,但对于我在某些情况下有效。

控制器

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

模型

    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>

3
为什么不优雅呢?它是强类型的,感觉非常清晰。我喜欢这种方法。+1 - T-moty

13
您正在尝试实现的行为并不适合使用AJAX。如果您只想更新页面的一部分,而不是完全重定向到其他页面,则最好使用AJAX。这实际上违背了AJAX的整个目的。
我建议您在描述的行为中不要使用AJAX。
或者,您可以尝试使用jQuery Ajax,它会提交请求,然后在请求完成时指定回调。在回调中,您可以确定它是失败还是成功,并在成功时重定向到另一个页面。我发现使用jQuery Ajax要容易得多,特别是因为我已经在使用该库进行其他操作。
您可以在此处找到有关jquery ajax的文档,但语法如下:
jQuery.ajax( options )  

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

jQuery.getJSON( url, data, callback )

jQuery.getScript( url, callback )

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

那么,只需向Delete操作执行常规POST请求,在大多数情况下它将成功,然后我可以重定向到主页索引页面。在失败的情况下,显示一个不同的视图,包含错误消息和用户采取适当行动的相应操作。听起来还好吗? - Jeff Widmer
是的,那听起来更有意义。 - Joseph
我按照Joseph的建议,将这个Ajax提交转换为常规POST。实际上,这样做效果更好,因为它给了我一个更清晰的方式来处理删除确认消息。 - Jeff Widmer

7

使用 JavaScript 肯定可以完成任务。

如果这更符合您的风格,也可以使用 Content

示例:

MVC 控制器

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

Javascript

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

谢谢!这真的帮了我很多。 - dee

5
您可以在Ajax成功时像下面这样简单地编写:

你可以像下面这样简单地在Ajax成功时写入:

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

3
这个怎么样:
public ActionResult GetGrid()
{
   string url = "login.html";
   return new HttpStatusCodeResult(System.Net.HttpStatusCode.Redirect,url)
}

接着

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

或者

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

1

我对Joseph提供的最佳答案不满意,因为他没有修复正确的问题,反而告诉我这是错误的用例。实际上有很多地方需要它,例如如果您正在将旧代码库转换为ajaxified代码,则需要它。在编程中,没有借口,因为编码的不仅仅是你,还有好的和坏的开发人员,你必须并肩工作。因此,如果我不在ajax中编写重定向,我的同行开发人员可能会强制我找到解决方案。就像我喜欢使用所有AMD模式站点或mvc4一样,我的公司可以让我远离它一年。

因此,现在让我们谈论解决方案。

我已经做了大量的ajax请求和响应处理,我找到的最简单的方法是向客户端发送状态代码,并拥有一个标准的javascript函数来理解这些代码。例如,如果我只是发送代码13,它可能意味着重定向。

因此,json响应如{ statusCode: 13, messsage: '/home/logged-in' } 当然还有许多变体提议,例如 { status: '成功', code: 13, url: '/home/logged-in', message: '您现在已登录' }

等等,所以根据您自己选择标准消息

通常我会从基础控制器类中继承,并按照我的标准响应进行选择,就像这样。
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);
    }

关于使用$.ajax而不是Ajax.BeginForm
我很喜欢使用Jquery ajax,但是我并不是世界上唯一做决定的人。
我的应用程序充满了Ajax.BeginForm,当然这不是我做的。但是我必须接受它。
所以在begin form中也有一个成功回调函数,你不需要使用jquery ajax来使用回调函数。
关于此的一些内容在这里Ajax.BeginForm, 调用Action, 返回JSON, 如何在OnSuccess JS函数中访问JSON对象? 谢谢

1
您可以使用$.ajaxSetup对传入的响应进行某种类型的Ajax响应过滤。如果响应包含MVC重定向,则可以在JS端评估此表达式。以下是JS的示例代码:
$.ajaxSetup({
    dataFilter: function (data, type) {
        if (data && typeof data == "string") {
            if (data.indexOf('window.location') > -1) {
                eval(data);
            }
        }
        return data;
    }
});

如果数据是:"window.location = '/Acount/Login'",上述过滤器将捕获并进行评估以进行重定向。

1

我需要这样做是因为我有一个ajax登录表单。当用户成功登录后,我会重定向到新页面并结束之前的请求,因为其他页面会处理重定向回依赖方(因为它是一个STS SSO系统)。

然而,我还希望它在禁用javascript时也能正常工作,因为它是中央登录跳转,所以我想出了这个方法,

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

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接