angularjs - ngsubmit - ¿Cómo puedo enviar la solicitud de nuevo en respuesta interceptor?
ng-href angular 5 (4)
He creado un interceptor en mi aplicación que detecta la pérdida de sesión (el servidor envía un HTTP 419). En este caso, necesito solicitar una nueva sesión del servidor, y luego me gustaría enviar la solicitud original de nuevo automáticamente.
Tal vez podría guardar la solicitud en un interceptor de solicitud y luego enviarla de nuevo, pero podría haber una solución más simple.
Tenga en cuenta que tengo que usar un servicio web específico para crear la sesión.
angular.module(''myapp'', [ ''ngResource'' ]).factory(
''MyInterceptor'',
function ($q, $rootScope) {
return function (promise) {
return promise.then(function (response) {
// do something on success
return response;
}, function (response) {
if(response.status == 419){
// session lost
// create new session server-side
// Session.query();
// then send current request again
// ???
}
return $q.reject(response);
});
};
}).config(function ($httpProvider) {
$httpProvider.responseInterceptors.push(''MyInterceptor'');
});
Aquí está mi solución usando promesas para los interesados. Básicamente, debe solicitar una nueva sesión y esperar la respuesta antes de enviar una nueva solicitud que corresponda a la solicitud original (utilizando response.config). Al devolver la promesa $ http (response.config), se asegura de que la respuesta se tratará como si fuera la solicitud original.
(la sintaxis puede no ser la mejor ya que soy nueva en las promesas)
angular.module(''myapp'', [ ''ngResource'' ]).factory(
''MyInterceptor'',
function ($q, $rootScope) {
return function (promise) {
return promise.then(function (response) {
// do something on success
return response;
}, function (response) {
if(response.status == 419){
// session lost
var Session = $injector.get(''Session'');
var $http = $injector.get(''$http'');
// first create new session server-side
var defer = $q.defer();
var promiseSession = defer.promise;
Session.query({},function(){
defer.resolve();
}, function(){
// error
defer.reject();
});
// and chain request
var promiseUpdate = promiseSession.then(function(){
return $http(response.config);
});
return promiseUpdate;
}
return $q.reject(response);
});
};
}).config(function ($httpProvider) {
$httpProvider.responseInterceptors.push(''MyInterceptor'');
});
El método responseError
de httpInterceptor
tiene que ser así:
responseError: function (response) {
// omit the retry if the request is made to a template or other url
if (response.config.apiCal === true) {
if (response.status === 419) {
var deferred = $q.defer();
// do something async: try to login.. rescue a token.. etc.
asyncFuncionToRecoverFrom419(funcion(){
// on success retry the http request
retryHttpRequest(response.config, deferred);
});
return deferred.promise;
} else {
// a template file...
return response;
}
}
}
Y la magia sucede aquí.
function retryHttpRequest(config, deferred){
function successCallback(response){
deferred.resolve(response);
}
function errorCallback(response){
deferred.reject(response);
}
var $http = $injector.get(''$http'');
$http(config).then(successCallback, errorCallback);
}
Está en el camino correcto, básicamente almacena la solicitud en una cola y la vuelve a intentar después de haber restablecido la sesión.
Echa un vistazo a este módulo popular: autenticación http http ( https://github.com/witoldsz/angular-http-auth ). En este módulo, interceptan 401 respuestas, pero puede modelar su solución a partir de este enfoque.
Más o menos la misma solución, traducida en mecanografiado:
/// <reference path="../app.ts" />
/// <reference path="../../scripts/typings/angularjs/angular.d.ts" />
class AuthInterceptorService {
static serviceId: string = "authInterceptorService";
constructor(private $q: ng.IQService, private $location: ng.ILocationService, private $injector, private $log: ng.ILogService, private authStatusService) {}
// Attenzione. Per qualche strano motivo qui va usata la sintassi lambda perché se no ts sbrocca il this.
public request = (config: ng.IRequestConfig) => {
config.headers = config.headers || {};
var s: AuthStatus = this.authStatusService.status;
if (s.isAuth) {
config.headers.Authorization = ''Bearer '' + s.accessToken;
}
return config;
}
public responseError = (rejection: ng.IHttpPromiseCallbackArg<any>) => {
if (rejection.status === 401) {
var that = this;
this.$log.warn("[AuthInterceptorService.responseError()]: not authorized request [401]. Now I try now to refresh the token.");
var authService: AuthService = this.$injector.get("authService");
var $http: ng.IHttpService = this.$injector.get("$http");
var defer = this.$q.defer();
var promise: ng.IPromise<any> = defer.promise.then(() => $http(rejection.config));
authService
.refreshAccessToken()
.then((response) => {
that.$log.info("[AuthInterceptorService.responseError()]: token refreshed succesfully. Now I resend the original request.");
defer.resolve();
},
(err) => {
that.$log.warn("[AuthInterceptorService.responseError()]: token refresh failed. I need to logout, sorry...");
this.authStatusService.clear();
this.$location.path(''/login'');
});
return promise;
}
return this.$q.reject(rejection);
}
}
// Update the app variable name to be that of your module variable
app.factory(AuthInterceptorService.serviceId,
["$q", "$location", "$injector", "$log", "authStatusService", ($q, $location, $injector, $log, authStatusService) => {
return new AuthInterceptorService($q, $location, $injector, $log, authStatusService)
}]);
Espero que esto ayude.