PAYATHON 2026

MVC 通过 $.ajax 返回 Payment view 后页面未更新

支付老李

结论

$.ajax() 只负责向后台发送请求并接收响应。即使 Action 返回了 View("Payment"),它也不会自动用返回的 HTML 替换当前页面,更不会让浏览器跳转到新页面。

如何显示 Payment,取决于你的实际需求:

  • 如果要跳转到完整页面,可以直接提交普通表单,或者让服务器返回目标 URL,再通过 window.location 跳转。
  • 如果只想更新页面的一部分,可以在 AJAX 的 success 回调中,把返回的 HTML 插入指定容器。

为什么页面没有变化

当 Controller 返回:

return View("Payment");

ASP.NET MVC 会将 Payment.cshtml 渲染为 HTML,并放入 HTTP 响应。

普通页面请求收到 HTML 后,浏览器会将其显示为新页面。AJAX 请求则不同,收到的响应只会交给 JavaScript。浏览器不知道应该把这段 HTML 放到哪里,所以开发者工具中可以看到完整的 View 响应,当前页面却没有变化。

同样,如果 AJAX 请求遇到服务器端的 Redirect(),浏览器通常只会在这次 AJAX 请求中跟随重定向,不会自动修改顶层页面的地址。

方案一:使用普通表单提交

如果操作完成后就应该进入完整的 Payment 页面,最简单可靠的做法是直接提交表单,不使用 AJAX:

<form action="@Url.Action("ConfirmFromPreview", "Confirm")" method="post">
    @Html.AntiForgeryToken()

    <!-- 表单字段 -->

    <button type="submit">确认</button>
</form>

Controller 可以直接返回 View:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmFromPreview(Occasions template)
{
    // 更新并保存 model

    return View("Payment");
}

这是一次正常的页面导航,浏览器会直接显示 Payment。

方案二:AJAX 成功后跳转

如果必须先通过 AJAX 保存数据,可以让 Action 返回 JSON,由服务器明确告诉前端接下来要跳转到哪个地址。

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ConfirmFromPreview(Occasions template)
{
    // 更新并保存 model

    return Json(new
    {
        success = true,
        redirectUrl = Url.Action("Payment", "Confirm")
    });
}

还需要为 Payment 提供一个可通过 GET 请求访问的 Action:

[HttpGet]
public ActionResult Payment()
{
    // 根据当前用户、订单 ID 或其他参数重新读取数据
    return View();
}

前端收到结果后再执行跳转:

$.ajax({
    url: '@Url.Action("ConfirmFromPreview", "Confirm")',
    type: 'POST',
    data: data,
    success: function (result) {
        if (result.success && result.redirectUrl) {
            window.location.href = result.redirectUrl;
        }
    },
    error: function (xhr) {
        console.error('请求失败:', xhr.status, xhr.responseText);
    }
});

这样处理比让 AJAX 直接接收完整页面的 HTML 更清楚。POST 用来修改数据,GET 用来显示页面,同时还能降低刷新页面时重复提交表单的风险。

如果 Payment 页面需要某个记录 ID,可以把它放进跳转地址:

return Json(new
{
    success = true,
    redirectUrl = Url.Action("Payment", "Confirm", new { id = template.Id })
});

方案三:把返回的 View 插入当前页面

如果 Payment.cshtml 只是当前页面的一部分,而不是一个完整页面,可以返回 Partial View:

[HttpPost]
public ActionResult ConfirmFromPreview(Occasions template)
{
    // 更新并保存 model

    return PartialView("Payment", template);
}

先在页面中准备一个容器:

<div id="payment-container"></div>

再在 AJAX 的成功回调中插入响应:

$.ajax({
    url: '@Url.Action("ConfirmFromPreview", "Confirm")',
    type: 'POST',
    data: data,
    success: function (html) {
        $('#payment-container').html(html);
    },
    error: function (xhr) {
        console.error('请求失败:', xhr.status, xhr.responseText);
    }
});

这种方式适合局部刷新。Payment.cshtml 最好不要包含完整的 <html>、<head> 和 <body> 结构,否则直接插入某个 <div> 后,可能产生无效的 DOM 结构,还可能导致样式或脚本重复加载。

不建议直接替换整个页面 HTML

从技术上说,可以这样处理 AJAX 返回的完整 HTML:

success: function (html) {
    document.open();
    document.write(html);
    document.close();
}

但通常不建议这么做。浏览器地址可能仍是原来的 URL,刷新、历史记录、相对资源路径和脚本生命周期也可能出现问题。如果需要显示完整页面,优先使用普通表单提交或 window.location.href。

其他注意事项

不要硬编码下面这种相对地址:

url: '../Confirm/ConfirmFromPreview'

一旦当前页面的路径层级发生变化,这个地址就可能失效。在 Razor View 中应优先使用:

url: '@Url.Action("ConfirmFromPreview", "Confirm")'

如果 Action 启用了防伪验证,还要把 token 一起提交:

var data = $('#confirm-form').serialize();

$.ajax({
    url: '@Url.Action("ConfirmFromPreview", "Confirm")',
    type: 'POST',
    data: data,
    success: function (result) {
        window.location.href = result.redirectUrl;
    }
});

表单中需要包含:

@Html.AntiForgeryToken()

问题不在于 MVC 是否成功渲染了 View,而在于请求是由 AJAX 发起的。AJAX 收到响应后,需要由前端代码决定是把 HTML 插入页面,还是跳转到新的地址。

备注:内容仅供参考。