Comment gérer une requête de redirection après un appel Ajax jQuery
j'utilise $.post()
pour appeler un servlet en utilisant Ajax et puis en utilisant le fragment HTML résultant pour remplacer un élément div
dans la page courante de l'utilisateur. Cependant, si la session se termine, le serveur envoie une directive de redirection pour envoyer l'utilisateur à la page de connexion. Dans ce cas, jQuery remplace l'élément div
par le contenu de la page de connexion, forçant les yeux de l'utilisateur à assister à une scène rare en effet.
comment gérer une redirection directive D'un appel Ajax avec jQuery 1.2.6?
30 réponses
j'ai lu cette question et j'ai mis en œuvre l'approche qui a été énoncée concernant le réglage du code d'état de réponse à 278 afin d'éviter que le navigateur manipule de façon transparente les redirections. Même si cela a fonctionné, j'étais un peu insatisfaite car c'est un peu un piratage.
après avoir creusé davantage autour, j'ai abandonné cette approche et utilisé JSON . Dans ce cas, toutes les réponses aux demandes ajax ont le code d'état 200 et le corps du message la réponse contient un objet JSON qui est construit sur le serveur. Le javascript sur le client peut alors utiliser L'objet JSON pour décider de ce qu'il doit faire.
j'ai eu un problème similaire au vôtre. J'exécute une requête ajax qui a 2 réponses possibles: une qui redirige le navigateur vers une nouvelle page et une qui remplace un formulaire HTML existant sur la page courante par un nouveau. Le code jquery pour faire ceci ressemble à quelque chose comme:
$.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);
}
}
});
The JSON objet "données" est construit sur le serveur pour avoir 2 membres: les données.redirection et données.forme. J'ai trouvé cette approche à être beaucoup mieux.
j'ai résolu ce problème par:
-
Ajouter un en-tête personnalisé à la réponse:
public ActionResult Index(){ if (!HttpContext.User.Identity.IsAuthenticated) { HttpContext.Response.AddHeader("REQUIRES_AUTH","1"); } return View(); }
-
liaison d'une fonction JavaScript au
ajaxSuccess
événement et vérification pour voir si l'en-tête existe:$(document).ajaxSuccess(function(event, request, settings) { if (request.getResponseHeader('REQUIRES_AUTH') === '1') { window.location = '/'; } });
aucun navigateur ne gère correctement les réponses 301 et 302. Et en fait la norme dit même qu'ils devraient les traiter "de manière transparente" ce qui est un énorme casse-tête pour les vendeurs de bibliothèque Ajax. Dans Ra-Ajax , nous avons été forcés d'utiliser le code de statut de réponse HTTP 278 (juste un code de succès "non utilisé") pour gérer de manière transparente les redirections du serveur...
cela m'ennuie vraiment, et si quelqu'un ici a un peu de "pull" dans le W3C j'apprécierais que vous pourrait laisser W3C savoir que nous avons vraiment besoin de gérer nous-mêmes les codes 301 et 302...! ;)
la solution qui a été finalement mis en œuvre était d'utiliser un wrapper pour la fonction de rappel de L'appel Ajax et dans ce wrapper vérifier l'existence d'un élément spécifique sur le morceau HTML retourné. Si l'élément a été trouvé alors le wrapper a exécuté une redirection. Si ce n'est pas le cas, l'expéditeur a transmis l'appel à la fonction de rappel réelle.
par exemple, notre fonction d'emballage était quelque chose comme:
function cbWrapper(data, funct){
if($("#myForm", data).length > 0)
top.location.href="login.htm";//redirection
else
funct(data);
}
puis, lors de la fabrication L'appel Ajax nous avons utilisé quelque chose comme:
$.post("myAjaxHandler",
{
param1: foo,
param2: bar
},
function(data){
cbWrapper(data, myActualCB);
},
"html"
);
cela a fonctionné pour nous car tous les appels Ajax retournaient toujours du HTML dans un élément DIV que nous utilisons pour remplacer un morceau de la page. Aussi, nous avons seulement besoin de rediriger vers la page de connexion.
j'aime la méthode de Timmerz avec un peu de citron. Si jamais vous obtenez retourné contentType de texte / html quand vous attendez JSON , vous êtes très probablement redirigé. Dans mon cas, je recharge simplement la page, et elle est redirigée vers la page de connexion. OH, et vérifiez que le statut jqXHR est 200, ce qui semble stupide, parce que vous êtes dans la fonction erreur, non? Dans le cas contraire, les erreurs légitimes force un processus itératif de rechargement (oups)
$.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();
}
});
utiliser le bas niveau $.ajax()
appeler:
$.ajax({
url: "/yourservlet",
data: { },
complete: function(xmlHttp) {
// xmlHttp is a XMLHttpRquest object
alert(xmlHttp.status);
}
});
essayez ceci pour une redirection:
if (xmlHttp.code != 200) {
top.location.href = '/some/other/page';
}
je voulais juste partager mon approche car cela pourrait aider quelqu'un:
j'ai essentiellement inclus un module JavaScript qui gère les choses d'authentification comme l'affichage du nom d'utilisateur et aussi ce cas de manipulation de la rediriger vers la page de connexion .
mon scénario: nous avons essentiellement un serveur ISA entre lequel écoute toutes les requêtes et répond avec un 302 et un en-tête de localisation sur notre page de connexion.
dans mon module JavaScript mon approche initiale était quelque chose comme
$(document).ajaxComplete(function(e, xhr, settings){
if(xhr.status === 302){
//check for location header and redirect...
}
});
le problème (comme beaucoup ici déjà mentionné) est que le navigateur gère la redirection par lui-même donc mon appel ajaxComplete
n'a jamais été appelé, mais à la place j'ai eu la réponse de la page de connexion déjà redirigée qui était évidemment un status 200
. Le problème: comment détecter si la réponse réussie 200 est votre page de connexion réelle ou juste une autre page arbitraire??
la solution
comme je n'ai pas pu capturer 302 réponses de redirection, j'ai ajouté un en-tête LoginPage
sur ma page de connexion qui contenait l'url de la page de connexion elle-même. Dans le module j'écoute Maintenant l'en-tête et je fais une redirection:
if(xhr.status === 200){
var loginPageRedirectHeader = xhr.getResponseHeader("LoginPage");
if(loginPageRedirectHeader && loginPageRedirectHeader !== ""){
window.location.replace(loginPageRedirectHeader);
}
}
...et qui fonctionne comme un charme :). Vous pourriez vous demander pourquoi j'inclus l'url dans l'en-tête LoginPage
...essentiellement parce que je n'ai trouvé aucun moyen de déterminer l'url de GET
résultant de la redirection automatique de l'emplacement de l'objet xhr
...
je sais que ce sujet est ancien, mais je vais donner encore une autre approche que j'ai trouvé et déjà décrit ici . En gros, J'utilise L'ASP.MVC avec WIF (mais ce n'est pas vraiment important pour le contexte de ce sujet - réponse est adéquate peu importe quels cadres sont utilisés. L'indice reste inchangé-traiter les questions liées aux échecs d'authentification lors de l'exécution des requêtes ajax) .
l'approche indiquée ci-dessous peut être appliqué à toutes les requêtes ajax hors de la boîte (si elles ne redéfinissent pas l'événement beforeSend évidemment).
$.ajaxSetup({
beforeSend: checkPulse,
error: function (XMLHttpRequest, textStatus, errorThrown) {
document.open();
document.write(XMLHttpRequest.responseText);
document.close();
}
});
avant que n'importe quelle requête ajax soit exécutée CheckPulse
la méthode est invoquée (la méthode de contrôleur qui peut être n'importe quoi plus simple):
[Authorize]
public virtual void CheckPulse() {}
si l'utilisateur n'est pas authentifié (token a expiré) cette méthode ne peut pas être utilisée (protégée par l'attribut Authorize
). Parce que le framework gère l'authentification, tout jeton expire, il met le statut http 302 à la réponse. Si vous ne voulez pas que votre navigateur gère la réponse 302 de manière transparente, attrapez-la dans Global.asax et changer l'état de la réponse-par exemple à 200 OK. En outre, ajouter en-tête, qui vous demande de traiter une telle réponse de manière spéciale (plus tard du côté du client):
protected void Application_EndRequest()
{
if (Context.Response.StatusCode == 302
&& (new HttpContextWrapper(Context)).Request.IsAjaxRequest())
{
Context.Response.StatusCode = 200;
Context.Response.AddHeader("REQUIRES_AUTH", "1");
}
}
enfin à côté du client vérifiez pour un tel en-tête personnalisé. Si présent-redirection complète à la page de connexion doit être fait (dans mon cas window.location
est remplacé par url from request qui est traité automatiquement par mon framework).
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;
}
}
});
}
je pense qu'une meilleure façon de gérer cela est de tirer parti des codes de réponse existants du protocole HTTP, en particulier 401 Unauthorized
.
Voici comment je l'ai résolu:
- côté serveur: si la session expire, et la requête est ajax. envoyer un en-tête de code de réponse 401
-
côté Client: se Lier à l'ajax événements
$('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 c'est plus générique et vous n'êtes pas Ecrire un nouvel en-tête / Spécification Personnalisé. Vous ne devriez pas non plus avoir à modifier vos appels ajax existants.
modifier: le commentaire de @Rob ci-dessous, 401 (le code de statut HTTP pour les erreurs d'authentification) devrait être l'indicateur. Voir 403 Forbidden vs 401 Unauthorized HTTP responses pour plus de détails. Cela étant dit, certains cadres web utilisent 403 à la fois pour les erreurs d'authentification et d'autorisation - donc s'adapter en conséquence. Grâce Rob.
j'ai résolu cette question comme ceci:
Ajouter un middleware pour traiter la réponse, si c'est une redirection pour une requête ajax, changer la réponse à une réponse normale avec l'url de redirection.
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
puis dans ajaxComplete, si la réponse contient redirect, ce doit être une redirect, donc changer l'emplacement du navigateur.
$('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;
}
}
}
la plupart des solutions données utilisent une solution de contournement, en utilisant un en-tête supplémentaire ou un code HTTP inadéquat. Ces solutions fonctionneront très probablement, mais elles semblent un peu "hacky". J'ai trouvé une autre solution.
nous utilisons WIF qui est configuré pour rediriger (passiveRedirectEnabled=" true") sur une réponse 401. La redirection est utile lors du traitement des requêtes normales mais ne fonctionnera pas pour les requêtes AJAX (puisque les navigateurs n'exécuteront pas la 302/redirect).
en utilisant le code suivant dans votre global.asax vous pouvez désactiver la redirection pour les requêtes 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;
}
}
cela vous permet de retourner 401 réponses pour des requêtes AJAX, que votre javascript peut ensuite gérer en rechargeant la page. Le rechargement de la page lancera un 401 qui sera traité par WIF (et WIF redirigera l'utilisateur vers la page de connexion).
un exemple de javascript pour gérer les erreurs 401:
$(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);
}
});
une autre solution que j'ai trouvée (particulièrement utile si vous voulez définir un comportement global) est d'utiliser la $.ajaxsetup()
méthode avec la statusCode
propriété . Comme d'autres l'ont fait remarquer, n'utilisez pas un statuscode de redirection ( 3xx
), utilisez plutôt un statuscode de 4xx
et gérez la redirection côté client.
$.ajaxSetup({
statusCode : {
400 : function () {
window.location = "/";
}
}
});
remplacer 400
par le statuscode que vous voulez manipuler. Comme déjà mentionné 401 Unauthorized
pourrait être une bonne idée. J'utilise le 400
car il est très imprécis et je peux utiliser le 401
pour des cas plus spécifiques (comme des identifiants de connexion erronés). Ainsi, au lieu de rediriger directement votre backend devrait retourner un code d'erreur 4xx
lorsque la session s'est arrêtée et que vous vous occupez de la redirection côté client. Fonctionne parfait pour moi, même avec des cadres comme l'épine dorsale.js
ce problème peut apparaître alors en utilisant ASP.NET MVC RedirectToAction method. Pour empêcher la forme d'afficher la réponse dans div, vous pouvez simplement faire une sorte de filtre ajax pour les réponses incomming avec $.ajaxSetup . Si la réponse contient la redirection MVC, vous pouvez évaluer cette expression du côté JS. Exemple de code pour JS ci-dessous:
$.ajaxSetup({
dataFilter: function (data, type) {
if (data && typeof data == "string") {
if (data.indexOf('window.location') > -1) {
eval(data);
}
}
return data;
}
});
Si des données est: de la fenêtre".emplacement = '/Acount / Login'" au-dessus du filtre l'attraper et évaluer pour faire de la redirection au lieu de laisser les données à afficher.
regroupant ce que Vladimir Prudnikov et Thomas Hansen ont dit:
- changez votre code côté serveur pour détecter s'il s'agit d'un XHR. Si c'est le code de réponse de la rediriger vers 278. À django:
if request.is_ajax(): response.status_code = 278
Cela fait que le navigateur traite la réponse comme un succès, et le passe à votre Javascript.
- dans votre JS, assurez-vous que la soumission du formulaire est via Ajax, vérifiez le code de réponse et redirigez si nécessaire:
$('#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); });
j'ai une solution simple qui fonctionne pour moi, aucun changement de code de serveur nécessaire...ajoutez juste une cuillère à thé de noix de muscade...
$(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 );
};
});
});
je vérifie la présence de la balise html, mais vous pouvez changer l'indexOf pour rechercher n'importe quelle chaîne de caractères unique existe dans votre page de connexion...
Essayer
$(document).ready(function () {
if ($("#site").length > 0) {
window.location = "<%= Url.Content("~") %>" + "Login/LogOn";
}
});
mettez-le sur la page de connexion. S'il a été chargé dans un div sur la page principale, il redirigera til la page de connexion. "#site " est un id d'un div qui est situé sur toutes les pages sauf la page de connexion.
<script>
function showValues() {
var str = $("form").serialize();
$.post('loginUser.html',
str,
function(responseText, responseStatus, responseXML){
if(responseStatus=="success"){
window.location= "adminIndex.html";
}
});
}
</script>
alors que les réponses semblent fonctionner pour les gens si vous utilisez la sécurité de printemps, j'ai trouvé l'extension LoginUrlAuthenticationEntryPoint et l'ajout de code spécifique pour gérer AJAX plus robuste. La plupart des exemples interceptent tous redirige pas seulement les échecs d'authentification. Cela n'était pas souhaitable pour le projet sur lequel je travaille. Vous pouvez trouver le besoin d'étendre aussi ExceptionTranslationFilter et d'outrepasser la méthode "sendstartathentication" pour supprimer l'étape de mise en cache si vous ne voulez pas que la requête AJAX échouée soit mise en cache.
Exemple 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"));
}
}
j'ai résolu cela en mettant ce qui suit dans mon login.php page.
<script type="text/javascript">
if (top.location.href.indexOf('login.php') == -1) {
top.location.href = '/login.php';
}
</script>
certains pourraient trouver utile ce qui suit:
je voulais que les clients soient redirigés vers la page de connexion pour toute action rest envoyée sans token d'autorisation. Comme toutes mes actions rest-sont basées sur Ajax, j'avais besoin d'une bonne façon générique de rediriger vers la page de connexion au lieu de gérer la fonction Ajax success.
C'est ce que j'ai fait:
sur N'importe quelle requête Ajax mon serveur retournera une réponse Json 200 "besoin de Authentifier" (si le client a besoin d'authentifier).
exemple Simple en Java (côté serveur):
@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());
}
}
}
dans Mon Javascript j'ai ajouté le code suivant:
$.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);
}
};
});
Et c'est tout.
Permettez-moi de citer à nouveau le problème décrit par @Steg
j'ai eu un problème similaire au vôtre. J'exécute une requête ajax qui a 2 réponses possibles: l'une qui redirige le navigateur vers une nouvelle page et un qui remplace un formulaire HTML existant sur la page courante par un nouveau un.
IMHO il s'agit d'un véritable défi et devra être officiellement étendu aux normes HTTP actuelles.
je crois que la nouvelle norme Http sera D'utiliser un nouveau code de statut.
signification: actuellement 301/302
dit au navigateur d'aller chercher le contenu de cette demande à un nouveau location
.
dans la norme étendue, il sera dit que si la réponse status: 308
(juste un exemple), alors le navigateur devrait rediriger la page principale vers le location
fourni.
cela étant dit, je suis enclin à déjà imiter ce comportement futur , et donc quand un document.la redirection est nécessaire, j'ai la réponse du serveur comme:
status: 204 No Content
x-status: 308 Document Redirect
x-location: /login.html
quand JS obtient le" status: 204
", il vérifie l'existence de l'en-tête x-status: 308
, et fait un document.rediriger vers la page fournie dans l'en-tête location
.
cela a-t-il un sens pour vous?
dans le servlet vous devez mettre
response.setStatus(response.SC_MOVED_PERMANENTLY);
pour envoyer le statut' 301 ' xmlHttp vous avez besoin d'une redirection...
et dans le $.fonction ajax vous ne devez pas utiliser la fonction .toString()
..., juste
if (xmlHttp.status == 301) {
top.location.href = 'xxxx.jsp';
}
le problème est qu'il n'est pas très souples, vous ne pouvez pas décider où vous voulez rediriger..
rediriger à travers les servlets devrait être le meilleur moyen. mais j'arrive toujours pas à trouver la bonne façon de le faire.
je voulais juste me raccrocher à toutes les requêtes ajax pour la page entière. @SuperG m'a fait commencer. Voici ce que j'ai:
// 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;
}
});
je voulais vérifier spécifiquement pour certains codes de statut http pour baser ma décision sur. Cependant, vous pouvez simplement lier à ajaxError pour obtenir autre chose que le succès (200 seulement peut-être?), Je pourrais avoir juste écrit:
$('body').bind('ajaxError', function(event,request,settings){
window.location.replace("http://mysite.tld/login");
}
si vous voulez aussi passer les valeurs, vous pouvez aussi définir les variables de session et l'accès Par exemple: Dans votre jsp vous pouvez écrire
<% HttpSession ses = request.getSession(true);
String temp=request.getAttribute("what_you_defined"); %>
et ensuite vous pouvez stocker cette valeur temp dans votre variable javascript et jouer autour de
Je n'ai pas eu de succès avec la solution d'en - tête-ils n'ont jamais été repris dans ma méthode ajaxSuccess / ajaxComplete. J'ai utilisé la réponse de Steg avec la réponse personnalisée, mais j'ai modifié le côté JS un peu. J'ai configuré une méthode que j'appelle dans chaque fonction pour que je puisse utiliser les méthodes standard $.get
et $.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);
}
}
exemple d'utilisation...
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;
}
enfin, je résous le problème en ajoutant une coutume HTTP Header
. Juste avant la réponse pour chaque requête côté serveur, j'ajoute l'url demandée courante à l'en-tête de la réponse.
Mon type d'application sur le serveur est Asp.Net MVC
, et il a un bon endroit pour le faire. dans Global.asax
j'ai mis en place le Application_EndRequest
événement de la sorte:
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);
}
}
c'est parfait pour moi! Maintenant dans chaque réponse du JQuery
$.post
j'ai le demandé url
et aussi d'autres en-têtes de réponse qui vient à la suite de POST
méthode par statut 302
, 303
,... .
et l'autre chose importante est qu'il n'est pas nécessaire de modifier le code côté serveur, ni du côté client.
et le suivant est la capacité d'accéder aux autres informations de l'action de post telles erreurs, messages, et ... De cette façon.
j'ai posté ceci, peut-être aider quelqu'un :)
j'avais ce problème sur une application django avec laquelle je bricole (disclaimer: Je Bricole pour apprendre, et je ne suis en aucune façon un expert). Ce que je voulais faire était d'utiliser jQuery ajax pour envoyer une demande de suppression à une ressource, la supprimer du côté du serveur, puis envoyer une redirection vers (essentiellement) la page d'accueil. Quand j'ai envoyé HttpResponseRedirect('/the-redirect/')
du script python, la méthode ajax de jQuery recevait 200 au lieu de 302. Donc, ce que j'ai fait était d'envoyer une réponse de 300 avec:
response = HttpResponse(status='300')
response['Location'] = '/the-redirect/'
return response
puis j'ai envoyé/traité la demande sur le client avec jQuery.ajax comme suit:
<button onclick="*the-jquery*">Delete</button>
where *the-jquery* =
$.ajax({
type: 'DELETE',
url: '/resource-url/',
complete: function(jqxhr){
window.location = jqxhr.getResponseHeader('Location');
}
});
peut-être qu'utiliser 300 n'est pas" juste", mais au moins ça a marché comme je le voulais.
PS: cela a été une énorme douleur d'éditer sur la version mobile de SO. Stupide FAI mis ma demande d'annulation de service par la droite quand j'ai été fait avec ma réponse!
vous pouvez également hook XMLHttpRequest envoyer prototype. Cela fonctionnera pour tous les messages (jQuery / dojo / etc)avec un gestionnaire.
j'ai écrit ce code pour gérer une erreur expirée de 500 pages, mais il devrait fonctionner aussi bien pour piéger une redirection de 200. Préparez l'entrée Wikipédia sur XMLHttpRequest onreadystatechange sur la signification de 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);
}
en outre, vous voudrez probablement rediriger l'utilisateur vers L'URL donnée dans headers. Alors finalement il ressemblera à ceci:
$.ajax({
//.... other definition
complete:function(xmlHttp){
if(xmlHttp.status.toString()[0]=='3'){
top.location.href = xmlHttp.getResponseHeader('Location');
}
});
UPD: Opps. La même tâche, mais il ne fonctionne. Faire ce genre de choses. Je te montrerai la solution quand je la trouverai.
j'ai obtenu une solution de travail en utilisant les réponses de @John et @Arpad link et @RobWinch link
J'utilise la sécurité des ressorts 3.2.9 et jQuery 1.10.2.
Étendre le Printemps de la classe à cause 4XX réponse seulement pour les requêtes AJAX:
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);
}
}
}
applicationContext-security.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>
dans mon JSPs, ajouter un gestionnaire D'erreurs AJAX global comme indiqué ici
$( document ).ajaxError(function( event, jqxhr, settings, thrownError ) {
if ( jqxhr.status === 403 ) {
window.location = "login.jsp";
} else {
if(thrownError != null) {
alert(thrownError);
} else {
alert("error");
}
}
});
également, supprimer les gestionnaires d'erreurs existants D'appels AJAX dans les pages JSP:
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");
// }
});
j'espère que ça aidera les autres.
Update1 J'ai trouvé que j'ai besoin d'ajouter l'option (toujours utiliser-par défaut-target="true") pour le formulaire de connexion config. Cela était nécessaire car après QU'une requête AJAX soit redirigée vers la page de connexion( en raison de session expirée), le printemps se souvient de la demande AJAX précédente et redirige automatiquement vers elle après la connexion. Cela provoque L'affichage du JSON retourné sur la page du navigateur. Bien sûr, pas ce que je veux.
Update2
Au lieu d'utiliser always-use-default-target="true"
, utilisez @RobWinch exemple de blocage des requêtes AJAX à partir de la requstCache. Cela permet aux liens normaux d'être redirigés vers leur cible d'origine après la connexion, mais AJAX aller à la page d'accueil après la connexion.