提问人:Elliot Vargas 提问时间:10/14/2008 最后编辑:John SlegersElliot Vargas 更新时间:9/29/2021 访问量:1137745
如何在 jQuery Ajax 调用后管理重定向请求
How to manage a redirect request after a jQuery Ajax call
问:
我曾经使用 Ajax 调用 servlet,然后使用生成的 HTML 片段来替换用户当前页面中的元素。但是,如果会话超时,服务器将发送重定向指令以将用户发送到登录页面。在这种情况下,jQuery将元素替换为登录页面的内容,迫使用户的眼睛目睹一个罕见的场景。$.post()
div
div
如何使用 jQuery 1.2.6 管理来自 Ajax 调用的重定向指令?
答:
使用低级调用:$.ajax()
$.ajax({
url: "/yourservlet",
data: { },
complete: function(xmlHttp) {
// xmlHttp is a XMLHttpRquest object
alert(xmlHttp.status);
}
});
尝试以下重定向:
if (xmlHttp.code != 200) {
top.location.href = '/some/other/page';
}
评论
此外,您可能希望将用户重定向到标头中给定的 URL。所以最后它看起来像这样:
$.ajax({
//.... other definition
complete:function(xmlHttp){
if(xmlHttp.status.toString()[0]=='3'){
top.location.href = xmlHttp.getResponseHeader('Location');
}
});
UPD:Opps。有同样的任务,但它不起作用。做这些事情。当我找到它时,我会向你展示解决方案。
评论
在servlet中,您应该发送重定向所需的“301”xmlHttp状态...response.setStatus(response.SC_MOVED_PERMANENTLY);
在 $.ajax 函数中,您不应该使用该函数...,只需.toString()
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
问题是它不是很灵活,你无法决定要重定向到哪里。
通过 servlet 重定向应该是最好的方法。但我仍然找不到正确的方法。
没有浏览器可以正确处理 301 和 302 响应。事实上,该标准甚至说他们应该“透明地”处理它们,这对 Ajax 库供应商来说是一个非常头疼的问题。在 Ra-Ajax 中,我们被迫使用 HTTP 响应状态代码 278(只是一些“未使用”的成功代码)来透明地处理来自服务器的重定向......
这真的让我很恼火,如果有人对 W3C 有一些“吸引力”,我希望你能让 W3C 知道我们真的需要自己处理 301 和 302 代码......!;)
评论
最终实现的解决方案是将包装器用于 Ajax 调用的回调函数,并在此包装器中检查返回的 HTML 块上是否存在特定元素。如果找到该元素,则包装器将执行重定向。如果没有,包装器会将调用转发到实际的回调函数。
例如,我们的包装函数是这样的:
function cbWrapper(data, funct){
if($("#myForm", data).length > 0)
top.location.href="login.htm";//redirection
else
funct(data);
}
然后,在进行 Ajax 调用时,我们使用了类似的东西:
$.post("myAjaxHandler",
{
param1: foo,
param2: bar
},
function(data){
cbWrapper(data, myActualCB);
},
"html"
);
这对我们有用,因为所有 Ajax 调用总是在我们用来替换页面一部分的 DIV 元素中返回 HTML。此外,我们只需要重定向到登录页面。
评论
我通过以下方式解决了这个问题:
将自定义标头添加到响应:
public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } return View(); }
将 JavaScript 函数绑定到
ajaxSuccess
事件并检查标头是否存在:$(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1') { window.location = '/'; } });
评论
弗拉基米尔·普鲁德尼科夫(Vladimir Prudnikov)和托马斯·汉森(Thomas Hansen)说:
- 更改服务器端代码以检测它是否为 XHR。如果是,请将重定向的响应代码设置为 278。 在 django 中:
if request.is_ajax(): response.status_code = 278
这使得浏览器将响应视为成功,并将其交给您的 Javascript。
- 在 JS 中,确保表单提交是通过 Ajax 提交的,检查响应代码并在需要时重定向:
$('#my-form').submit(function(event){ event.preventDefault(); var options = { url: $(this).attr('action'), type: 'POST', complete: function(response, textStatus) { if (response.status == 278) { window.location = response.getResponseHeader('Location') } else { ... your code here ... } }, data: $(this).serialize(), }; $.ajax(options); });
我阅读了这个问题并实施了有关将响应 HTTP 状态代码设置为 278 的方法,以避免浏览器透明地处理重定向。尽管这有效,但我有点不满意,因为它有点黑客攻击。
经过更多的挖掘,我放弃了这种方法,转而使用 JSON。在这种情况下,对 AJAX 请求的所有响应都具有状态代码 200,并且响应的正文包含在服务器上构造的 JSON 对象。然后,客户端上的 JavaScript 可以使用 JSON 对象来决定它需要做什么。
我有一个和你类似的问题。我执行了一个 AJAX 请求,该请求有 2 种可能的响应:一种是将浏览器重定向到新页面,另一种是用新页面替换当前页面上的现有 HTML 表单。执行此操作的 jQuery 代码如下所示:
$.ajax({
type: "POST",
url: reqUrl,
data: reqBody,
dataType: "json",
success: function(data, textStatus) {
if (data.redirect) {
// data.redirect contains the string URL to redirect to
window.location.href = data.redirect;
} else {
// data.form contains the HTML for the replacement form
$("#myform").replaceWith(data.form);
}
}
});
JSON 对象 “data” 在服务器上构造为具有 2 个成员:和 .我发现这种方法要好得多。data.redirect
data.form
评论
我只是想锁定整个页面的任何ajax请求。@SuperG让我开始了。这是我最终得到的:
// redirect ajax requests that are redirected, not found (404), or forbidden (403.)
$('body').bind('ajaxComplete', function(event,request,settings){
switch(request.status) {
case 301: case 404: case 403:
window.location.replace("http://mysite.tld/login");
break;
}
});
我想专门检查某些 http 状态代码作为我决定的基础。但是,您可以绑定到 ajaxError 以获得成功以外的任何结果(也许只有 200 个?我本可以写:
$('body').bind('ajaxError', function(event,request,settings){
window.location.replace("http://mysite.tld/login");
}
评论
我有一个简单的解决方案,不需要更改服务器代码......只需加入一小匙肉豆蔻......
$(document).ready(function ()
{
$(document).ajaxSend(
function(event,request,settings)
{
var intercepted_success = settings.success;
settings.success = function( a, b, c )
{
if( request.responseText.indexOf( "<html>" ) > -1 )
window.location = window.location;
else
intercepted_success( a, b, c );
};
});
});
我检查了html标签的存在,但是您可以更改indexOf以搜索登录页面中存在的任何唯一字符串...
评论
尝试
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
把它放在登录页面上。如果它被加载到主页上的 div 中,它将重定向到登录页面。“#site”是 div 的 ID,它位于除登录页面之外的所有页面上。
我喜欢 Timmerz 的方法,带有轻微的柠檬味。如果你在需要 JSON 时收到返回的 text/html 的 contentType,你很可能被重定向了。就我而言,我只是简单地重新加载页面,然后它就会被重定向到登录页面。哦,检查 jqXHR 状态是否为 200,这似乎很愚蠢,因为你在错误函数中,对吧?否则,合法的错误情况将强制迭代重新加载(哎呀)
$.ajax(
error: function (jqXHR, timeout, message) {
var contentType = jqXHR.getResponseHeader("Content-Type");
if (jqXHR.status === 200 && contentType.toLowerCase().indexOf("text/html") >= 0) {
// assume that our login has expired - reload our current page
window.location.reload();
}
});
评论
我在我正在修补的 django 应用程序上遇到了这个问题(免责声明:我正在修补学习,绝不是专家)。我想做的是使用 jQuery ajax 向资源发送 DELETE 请求,在服务器端将其删除,然后发送重定向回(基本上)主页。当我从 python 脚本发送时,jQuery 的 ajax 方法接收的是 200 而不是 302。所以,我所做的是发送 300 的回复:HttpResponseRedirect('/the-redirect/')
response = HttpResponse(status='300')
response['Location'] = '/the-redirect/'
return response
然后我使用 jQuery.ajax 在客户端上发送/处理请求,如下所示:
<button onclick="*the-jquery*">Delete</button>
where *the-jquery* =
$.ajax({
type: 'DELETE',
url: '/resource-url/',
complete: function(jqxhr){
window.location = jqxhr.getResponseHeader('Location');
}
});
也许使用 300 不是“正确的”,但至少它就像我想要的那样工作。
PS :在SO的移动版上编辑这是一个巨大的痛苦。愚蠢的ISP在我完成回答后立即提出了我的服务取消请求!
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if(responseStatus=="success"){
window.location= "adminIndex.html";
}
});
}
</script>
I solved this by putting the following in my login.php page.
<script type="text/javascript">
if (top.location.href.indexOf('login.php') == -1) {
top.location.href = '/login.php';
}
</script>
I resolved this issue like this:
Add a middleware to process response, if it is a redirect for an ajax request, change the response to a normal response with the redirect url.
class AjaxRedirect(object):
def process_response(self, request, response):
if request.is_ajax():
if type(response) == HttpResponseRedirect:
r = HttpResponse(json.dumps({'redirect': response['Location']}))
return r
return response
Then in ajaxComplete, if the response contains redirect, it must be a redirect, so change the browser's location.
$('body').ajaxComplete(function (e, xhr, settings) {
if (xhr.status == 200) {
var redirect = null;
try {
redirect = $.parseJSON(xhr.responseText).redirect;
if (redirect) {
window.location.href = redirect.replace(/\?.*$/, "?next=" + window.location.pathname);
}
} catch (e) {
return;
}
}
}
您还可以钩接 XMLHttpRequest 发送原型。这将适用于使用一个处理程序的所有发送(jQuery/dojo/etc)。
我编写此代码是为了处理 500 页过期错误,但它应该同样适用于捕获 200 重定向。准备好 XMLHttpRequest onreadystatechange 上关于 readyState 含义的维基百科条目。
// Hook XMLHttpRequest
var oldXMLHttpRequestSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
//console.dir( this );
this.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 500 && this.responseText.indexOf("Expired") != -1) {
try {
document.documentElement.innerHTML = this.responseText;
} catch(error) {
// IE makes document.documentElement read only
document.body.innerHTML = this.responseText;
}
}
};
oldXMLHttpRequestSend.apply(this, arguments);
}
我只是想分享我的方法,因为这可能会对某人有所帮助:
我基本上包含一个 JavaScript 模块,该模块处理身份验证内容,例如显示用户名以及处理重定向到登录页面的情况。
我的场景:我们基本上有一个 ISA 服务器,它侦听所有请求,并使用 302 和位置标头响应我们的登录页面。
在我的 JavaScript 模块中,我最初的方法是这样的
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
问题(正如这里的许多人已经提到的那样)是浏览器自行处理重定向,因此我的回调从未被调用,而是我得到了已经重定向的登录页面的响应,这显然是一个.问题:您如何检测成功的 200 响应是您的实际登录页面还是只是其他任意页面?ajaxComplete
status 200
解决方案
由于我无法捕获 302 重定向响应,因此我在登录页面上添加了一个标题,其中包含登录页面本身的 URL。在模块中,我现在侦听标头并执行重定向:LoginPage
if(xhr.status === 200){
var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
window.location.replace(loginPageRedirectHeader);
}
}
...这就像魅力:)一样。您可能想知道为什么我在标题中包含 url......好吧,基本上是因为我发现无法确定从对象自动位置重定向产生的 url......LoginPage
GET
xhr
评论
X-
X-LoginPage: http://example.com/login
X-
我认为处理此问题的更好方法是利用现有的HTTP协议响应代码,特别是.401 Unauthorized
这是我解决它的方法:
- 服务器端:如果会话过期,并且请求是 ajax。发送 401 响应代码标头
客户端:绑定到 ajax 事件
$('body').bind('ajaxSuccess',function(event,request,settings){ if (401 == request.status){ window.location = '/users/login'; } }).bind('ajaxError',function(event,request,settings){ if (401 == request.status){ window.location = '/users/login'; } });
IMO 这是更通用的,您没有编写一些新的自定义规范/标头。您也不必修改任何现有的 ajax 调用。
编辑:根据@Rob在下面的评论,401(身份验证错误的 HTTP 状态代码)应该是指示器。有关详细信息,请参阅 403 禁止访问与 401 未经授权的 HTTP 响应。话虽如此,一些 Web 框架将 403 用于身份验证和授权错误 - 因此请相应地进行调整。谢谢罗伯。
评论
如果您还想传递这些值,那么您还可以设置会话变量和访问权限 例如: 在你的jsp中,你可以写
<% HttpSession ses = request.getSession(true);
String temp=request.getAttribute("what_you_defined"); %>
然后你可以将这个临时值存储在你的 javascript 变量中并玩
我在标头解决方案上没有任何成功 - 它们从未在我的 ajaxSuccess / ajaxComplete 方法中被拾取。我将 Steg 的答案与自定义响应一起使用,但我对 JS 端进行了一些修改。我设置了一个在每个函数中调用的方法,以便我可以使用标准和方法。$.get
$.post
function handleAjaxResponse(data, callback) {
//Try to convert and parse object
try {
if (jQuery.type(data) === "string") {
data = jQuery.parseJSON(data);
}
if (data.error) {
if (data.error == 'login') {
window.location.reload();
return;
}
else if (data.error.length > 0) {
alert(data.error);
return;
}
}
}
catch(ex) { }
if (callback) {
callback(data);
}
}
使用示例...
function submitAjaxForm(form, url, action) {
//Lock form
form.find('.ajax-submit').hide();
form.find('.loader').show();
$.post(url, form.serialize(), function (d) {
//Unlock form
form.find('.ajax-submit').show();
form.find('.loader').hide();
handleAjaxResponse(d, function (data) {
// ... more code for if auth passes ...
});
});
return false;
}
我知道这个话题已经很老了,但我会给出另一种我发现并之前在这里描述过的方法。基本上我使用的是 ASP。带有 WIF 的 MVC(但这对于本主题的上下文并不重要 - 无论使用哪种框架,答案都足够了。线索保持不变 - 在执行 ajax 请求时处理与身份验证失败相关的问题)。
下面显示的方法可以应用于所有开箱即用的 ajax 请求(如果它们显然没有重新定义 beforeSend 事件)。
$.ajaxSetup({
beforeSend: checkPulse,
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.open();
document.write(XMLHttpRequest.responseText);
document.close();
}
});
在执行任何 ajax 请求之前,都会调用方法(控制器方法,可以是最简单的方法):CheckPulse
[Authorize]
public virtual void CheckPulse() {}
如果用户未通过身份验证(令牌已过期),则无法访问此类方法(受属性保护)。由于框架处理身份验证,因此在令牌过期时,它会将 http 状态 302 放入响应。如果不希望浏览器以透明方式处理 302 响应,请在 Global.asax 中捕获它并更改响应状态 - 例如,更改为 200 OK。 此外,添加标头,指示您以特殊方式处理此类响应(稍后在客户端):Authorize
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302
&& (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
{
Context.Response.StatusCode = 200;
Context.Response.AddHeader("REQUIRES_AUTH", "1");
}
}
最后,在客户端检查此类自定义标头。如果存在 - 应该完全重定向到登录页面(在我的情况下,由请求中的 url 替换,由我的框架自动处理)。window.location
function checkPulse(XMLHttpRequest) {
var location = window.location.href;
$.ajax({
url: "/Controller/CheckPulse",
type: 'GET',
async: false,
beforeSend: null,
success:
function (result, textStatus, xhr) {
if (xhr.getResponseHeader('REQUIRES_AUTH') === '1') {
XMLHttpRequest.abort(); // terminate further ajax execution
window.location = location;
}
}
});
}
评论
虽然如果您使用的是 Spring Security,答案似乎对人们有用,但我发现扩展 LoginUrlAuthenticationEntryPoint 并添加特定代码来处理 AJAX 更加健壮。大多数示例会拦截所有重定向,而不仅仅是身份验证失败。这对于我所从事的项目来说是不可取的。如果您不希望缓存失败的 AJAX 请求,您可能还会发现还需要扩展 ExceptionTranslationFilter 并重写“sendStartAuthentication”方法以删除缓存步骤。
示例 AjaxAwareAuthenticationEntryPoint:
public class AjaxAwareAuthenticationEntryPoint extends
LoginUrlAuthenticationEntryPoint {
public AjaxAwareAuthenticationEntryPoint(String loginUrl) {
super(loginUrl);
}
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
if (isAjax(request)) {
response.sendError(HttpStatus.UNAUTHORIZED.value(), "Please re-authenticate yourself");
} else {
super.commence(request, response, authException);
}
}
public static boolean isAjax(HttpServletRequest request) {
return request != null && "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
评论
大多数给定的解决方案都使用一种解决方法,即使用额外的标头或不适当的 HTTP 代码。这些解决方案很可能会奏效,但感觉有点“笨拙”。我想出了另一个解决方案。
我们使用的是 WIF,它配置为在 401 响应上重定向 (passiveRedirectEnabled=“true”)。重定向在处理正常请求时很有用,但不适用于 AJAX 请求(因为浏览器不会执行 302/重定向)。
在 global.asax 中使用以下代码,可以禁用 AJAX 请求的重定向:
void WSFederationAuthenticationModule_AuthorizationFailed(object sender, AuthorizationFailedEventArgs e)
{
string requestedWithHeader = HttpContext.Current.Request.Headers["X-Requested-With"];
if (!string.IsNullOrEmpty(requestedWithHeader) && requestedWithHeader.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
{
e.RedirectToIdentityProvider = false;
}
}
这允许您返回 AJAX 请求的 401 响应,然后您的 javascript 可以通过重新加载页面来处理这些响应。重新加载页面将引发 401,该 401 将由 WIF 处理 (并且 WIF 会将用户重定向到登录页面) 。
处理 401 错误的 javascript 示例:
$(document).ajaxError(function (event, jqxhr, settings, exception) {
if (jqxhr.status == 401) { //Forbidden, go to login
//Use a reload, WIF will redirect to Login
location.reload(true);
}
});
评论
这对我有用:
success: function(data, textStatus, xhr) {
console.log(xhr.status);
}
成功后,Ajax 将获得与浏览器从服务器获取的相同状态代码并执行它。
评论
我发现的另一种解决方案(如果要设置全局行为特别有用)是将 $.ajaxsetup()
方法与 statusCode
属性一起使用。正如其他人指出的那样,不要使用重定向状态代码(),而是使用状态代码并处理重定向客户端。3xx
4xx
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
替换为要处理的状态代码。如前所述,这可能是一个好主意。我使用 the,因为它非常不具体,我可以将 用于更具体的情况(例如错误的登录凭据)。因此,当会话超时时,您的后端应该返回错误代码,而不是直接重定向,并且您处理重定向客户端。即使使用像 backbone.js 这样的框架,对我来说也很完美400
401 Unauthorized
400
401
4xx
评论
最后,我通过添加自定义来解决问题。在服务器端响应每个请求之前,我将当前请求的 url 添加到响应的标头中。HTTP Header
我在服务器上的应用程序类型是 ,它有一个很好的地方来做这件事。在我实现事件时:Asp.Net MVC
Global.asax
Application_EndRequest
public class MvcApplication : System.Web.HttpApplication
{
// ...
// ...
protected void Application_EndRequest(object sender, EventArgs e)
{
var app = (HttpApplication)sender;
app.Context.Response.Headers.Add("CurrentUrl",app.Context. Request.CurrentExecutionFilePath);
}
}
它非常适合我!现在,在 i 的每个响应中,都有请求的响应头,以及其他响应标头,这些标头是按状态方法得出的结果,,... .JQuery
$.post
url
POST
302
303
其他重要的是,无需在服务器端或客户端修改代码。
接下来是以这种方式访问发布操作的其他信息(例如错误、消息和...)的能力。
我发布了这个,也许可以帮助某人:)
有些人可能会发现以下内容很有用:
我希望将客户端重定向到登录页面,以便在没有授权令牌的情况下发送任何休息操作。由于我所有的休息操作都是基于 Ajax 的,因此我需要一种很好的通用方法来重定向到登录页面,而不是处理 Ajax 成功函数。
这是我所做的:
在任何 Ajax 请求中,我的服务器都会返回 Json 200 响应“NEED TO AUTHENTICATE”(如果客户端需要身份验证)。
Java 中的简单示例(服务器端):
@Secured
@Provider
@Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
private final Logger m_logger = LoggerFactory.getLogger(AuthenticationFilter.class);
public static final String COOKIE_NAME = "token_cookie";
@Override
public void filter(ContainerRequestContext context) throws IOException {
// Check if it has a cookie.
try {
Map<String, Cookie> cookies = context.getCookies();
if (!cookies.containsKey(COOKIE_NAME)) {
m_logger.debug("No cookie set - redirect to login page");
throw new AuthenticationException();
}
}
catch (AuthenticationException e) {
context.abortWith(Response.ok("\"NEED TO AUTHENTICATE\"").type("json/application").build());
}
}
}
在我的 Javascript 中,我添加了以下代码:
$.ajaxPrefilter(function(options, originalOptions, jqXHR) {
var originalSuccess = options.success;
options.success = function(data) {
if (data == "NEED TO AUTHENTICATE") {
window.location.replace("/login.html");
}
else {
originalSuccess(data);
}
};
});
仅此而已。
然后使用 MVC RedirectToAction 方法 ASP.NET 可能会出现此问题。为了防止表单在 div 中显示响应,您可以简单地执行某种 ajax 响应过滤器,以使用 $.ajaxSetup 接收响应。如果响应包含 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'”,上面的过滤器将捕获它并评估以进行重定向,而不是让数据显示。
评论
data
是否在响应正文或标头中?
我使用@John的答案得到了一个有效的解决方案,@Arpad链接和@RobWinch链接
我使用 Spring Security 3.2.9 和 jQuery 1.10.2。
扩展 Spring 的类,使其仅从 AJAX 请求中引起 4XX 响应:
public class CustomLoginUrlAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint {
public CustomLoginUrlAuthenticationEntryPoint(final String loginFormUrl) {
super(loginFormUrl);
}
// For AJAX requests for user that isn't logged in, need to return 403 status.
// For normal requests, Spring does a (302) redirect to login.jsp which the browser handles normally.
@Override
public void commence(final HttpServletRequest request,
final HttpServletResponse response,
final AuthenticationException authException)
throws IOException, ServletException {
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
} else {
super.commence(request, response, authException);
}
}
}
应用程序上下文安全.xml
<security:http auto-config="false" use-expressions="true" entry-point-ref="customAuthEntryPoint" >
<security:form-login login-page='/login.jsp' default-target-url='/index.jsp'
authentication-failure-url="/login.jsp?error=true"
/>
<security:access-denied-handler error-page="/errorPage.jsp"/>
<security:logout logout-success-url="/login.jsp?logout" />
...
<bean id="customAuthEntryPoint" class="com.myapp.utils.CustomLoginUrlAuthenticationEntryPoint" scope="singleton">
<constructor-arg value="/login.jsp" />
</bean>
...
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache">
<property name="requestMatcher">
<bean class="org.springframework.security.web.util.matcher.NegatedRequestMatcher">
<constructor-arg>
<bean class="org.springframework.security.web.util.matcher.MediaTypeRequestMatcher">
<constructor-arg>
<bean class="org.springframework.web.accept.HeaderContentNegotiationStrategy"/>
</constructor-arg>
<constructor-arg value="#{T(org.springframework.http.MediaType).APPLICATION_JSON}"/>
<property name="useEquals" value="true"/>
</bean>
</constructor-arg>
</bean>
</property>
</bean>
在我的 JSP 中,添加一个全局 AJAX 错误处理程序,如下所示
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
此外,从 JSP 页面中的 AJAX 调用中删除现有的错误处理程序:
var str = $("#viewForm").serialize();
$.ajax({
url: "get_mongoDB_doc_versions.do",
type: "post",
data: str,
cache: false,
async: false,
dataType: "json",
success: function(data) { ... },
// error: function (jqXHR, textStatus, errorStr) {
// if(textStatus != null)
// alert(textStatus);
// else if(errorStr != null)
// alert(errorStr);
// else
// alert("error");
// }
});
我希望它能帮助其他人。
更新1我发现我需要将选项(always-use-default-target=“true”)添加到form-login配置中。 这是必需的,因为在 AJAX 请求被重定向到登录页面(由于会话过期)后,Spring 会记住之前的 AJAX 请求并在登录后自动重定向到它。这会导致返回的 JSON 显示在浏览器页面上。当然,不是我想要的。
更新2不要使用 ,而是使用阻止来自 requstCache 的 AJAX 请求@RobWinch示例。这允许普通链接在登录后重定向到其原始目标,但 AJAX 在登录后转到主页。always-use-default-target="true"
让我再次引用@Steg所描述的问题
我有一个和你类似的问题。我执行了一个有 2 个 可能的响应:将浏览器重定向到新页面的响应,以及 一种将当前页面上的现有 HTML 表单替换为新的 一。
恕我直言,这是一个真正的挑战,必须正式扩展到当前的HTTP标准。
我相信新的 Http 标准将使用新的状态代码。
意思是:当前告诉浏览器去获取这个请求的内容到一个新的.301/302
location
在扩展标准中,它会说如果响应(只是一个示例),那么浏览器应该将主页重定向到提供的页面。status: 308
location
话虽如此;我倾向于已经模仿这种未来的行为,因此当需要 document.redirect 时,我让服务器响应如下:
status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html
当 JS 获得 “” 时,它会检查标头是否存在,并执行 document.redirect 到标头中提供的页面。status: 204
x-status: 308
location
这对你有意义吗?
使用状态代码选项,如下图所示,重定向通常是 301、302 重定向状态代码。
$.ajax({
type: <HTTP_METHOD>,
url: {server.url},
data: {someData: true},
statusCode: {
301: function(responseObject, textStatus, errorThrown) {
//yor code goes here
},
302: function(responseObject, textStatus, errorThrown) {
//yor code goes here
}
}
})
.done(function(data){
alert(data);
})
.fail(function(jqXHR, textStatus){
alert('Something went wrong: ' + textStatus);
})
.always(function(jqXHR, textStatus) {
alert('Ajax request was finished')
});
评论
后端弹簧。@ExceptionHandler
- 400 和业务相关异常的错误字符串(将显示在弹出窗口中)
- 302 和 Location 标头到错误/登录页面,用于浏览器请求的应用程序异常(由浏览器自动重定向)
- 500/400 和 Location 标头到错误/登录页面,用于通过 ajax 回调重定向的 ajax 请求
通过用户会话传递到错误页面的异常详细信息
@Order(HIGHEST_PRECEDENCE)
public class ExceptionHandlerAdvise {
private static Logger logger = LoggerFactory.getLogger(ExceptionHandlerAdvise.class);
@Autowired
private UserInfo userInfo;
@ExceptionHandler(value = Exception.class)
protected ResponseEntity<Object> handleException(Exception ex, WebRequest request) {
HttpHeaders headers = new HttpHeaders();
if (isBusinessException(ex)) {
logger.warn(getRequestURL(request), ex);
return new ResponseEntity<>(getUserFriendlyErrorMessage(ex), headers, BAD_REQUEST);
} else {
logger.error(getRequestURL(request), ex);
userInfo.setLastError(ex);
headers.add("Location", "/euc-portal/fault");
return new ResponseEntity<>(null, headers, isAjaxRequest(request) ? INTERNAL_SERVER_ERROR : FOUND);
}
}
}
private boolean isAjaxRequest(WebRequest request) {
return request.getHeader("x-requested-with") != null;
}
private String getRequestURL(WebRequest request) {
if (request instanceof ServletWebRequest) {
HttpServletRequest servletRequest = ((ServletWebRequest) request).getRequest();
StringBuilder uri = new StringBuilder(servletRequest.getRequestURI());
if (servletRequest.getQueryString() != null) {
uri.append("?");
uri.append(servletRequest.getQueryString());
}
return uri.toString();
}
return request.getContextPath();
}
LoginHandlerInterceptor
@Service
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Autowired
private UserInfo userInfo;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (userInfo.getPrincipal() == null && !(request.getRequestURI().contains(LOGIN_URL) || request.getRequestURI().contains(FAULT_URL) || request.getRequestURI().startsWith("/app/css"))) {
response.addHeader("Location", LOGIN_URL);
response.setStatus(isAjaxRequest(request) ? BAD_REQUEST.value() : FOUND.value());
return false;
}
return true;
}
}
客户端代码
$.post('/app/request', params).done(function(response) {
...
}).fail(function(response) {
if (response.getResponseHeader('Location')) {
window.top.location.href = response.getResponseHeader('Location');
return;
}
alert(response);
});
作为 ajax 的替代方案,正在开发一个新的 Fetch API,它允许手动重定向处理。您需要检查当前的浏览器支持是否足以满足您的需求。
评论
HttpContext.Response.AddHeader