J'utilise Angular 2.1.2.
J'ai un jeton d'authentification (utilisant angular2-jwt) et s'il expire, mon appel webApi échoue avec une erreur 401. Je recherche une solution où l'utilisateur ne perdra aucune donnée d'entrée.
Je peux attraper ce 401 et ouvrir un modal avec la connexion. L'utilisateur se connecte ensuite, le modal disparaît et il voit son écran de saisie. Cependant, les demandes ayant échoué affichent des erreurs, je dois donc retraiter les demandes. S'il s'agissait d'un routeur, les données initiales n'ont pas été chargées.
Je pourrais recharger la page, mais si je router.navigate sur la même page, il ne semble pas vraiment recharger la page. Je ne veux pas recharger une page entière sur l'application d'une seule page. Existe-t-il un moyen de forcer router.navigate à s'exécuter, même s'il s'agit de la page actuelle?
La navigation est toujours un problème car je perdrais toutes les nouvelles données d'entrée qui n'ont pas été enregistrées.
Idéalement, la demande serait simplement "suspendue" jusqu'à ce que l'utilisateur se connecte à partir du modal. Je n'ai pas trouvé de moyen de mettre cela en œuvre.
Des idées? Existe-t-il une meilleure pratique?
Habituellement, je fournirais moi-même un HttpService
au lieu d'utiliser Http
directement. Donc, avec votre exigence, je peux fournir ma propre méthode get()
pour enchaîner l'authentification avant d'envoyer de vraies requêtes HTTP.
Voici le service:
@Injectable()
class HttpService {
constructor(private http: Http, private auth: Authentication) {}
public get(url: string): Observable<Response> {
return this.auth.authenticate().flatMap(authenticated => {
if (authenticated) {
return this.http.get(url);
}
else {
return Observable.throw('Unable to re-authenticate');
}
});
}
}
Voici le composant pour appeler le service:
@Component({
selector: 'my-app',
template: `<h1>Hello {{name}}</h1>
<button (click)="doSomething()">Do Something</button>
<div [hidden]="!auth.showModal">
<p>Do you confirm to log in?</p>
<button (click)="yes()">Yes</button><button (click)="no()">No</button>
</div>
`,
})
export class AppComponent {
name = 'Angular';
constructor(private httpSvc: HttpService, public auth: Authentication) {}
ngOnInit() {
}
doSomething() {
let a = this.httpSvc.get('hello.json').subscribe(() => {
alert('Data retrieved!');
}, err => {
alert(err);
});
}
yes() {
this.auth.confirm.emit(true);
}
no() {
this.auth.confirm.emit(false);
}
}
En enchaînant les observables, le service Authentication
détermine s'il faut interrompre le flux normal pour afficher le modal (bien qu'il ne vive actuellement qu'avec le composant App, il peut certainement être implémenté séparément). Et une fois qu'une réponse positive est reçue de la boîte de dialogue, le service peut reprendre le flux.
class Authentication {
public needsAuthentication = true;
public showModal = false;
public confirm = new EventEmitter<boolean>();
public authenticate(): Observable<boolean> {
// do something to make sure authentication token works correctly
if (this.needsAuthentication) {
this.showModal = true;
return Observable.create(observer => {
this.confirm.subscribe(r => {
this.showModal = false;
this.needsAuthentication = !r;
observer.next(r);
observer.complete();
});
});
}
else {
return Observable.of(true);
}
}
}
J'ai un exemple complet en direct ici.
http://plnkr.co/edit/C129guNJvri5hbGZGsHp?open=app%2Fapp.component.ts&p=preview
Après avoir effectué une connexion et reçu un jeton (qui, dans mon cas, expire dans 60 minutes), j'ai défini un intervalle qui vérifie chaque minute pour voir si 59 minutes se sont écoulées. Si oui, j'ouvre une boîte de dialogue de connexion.
L'idée, cependant, est que la boîte de dialogue de connexion n'est pas un itinéraire, mais juste une superposition qui s'ouvre au-dessus de l'écran sur lequel l'utilisateur se trouve (ainsi, je mets le composant de connexion dans le code HTML du composant racine de l'application et j'utilise un observable pour appeler la boîte de dialogue de connexion de n'importe où dans l'application).
Lorsque l'utilisateur se reconnecte correctement, je ferme la boîte de dialogue de connexion et l'utilisateur poursuit gaiement son chemin avec l'écran qu'il utilisait avant de se reconnecter.
Je ne sais pas si c'est une "meilleure pratique" mais cela fonctionne dans la situation que vous décrivez. Faites-le moi savoir et je pourrai mettre du code.
Idéalement, la demande serait simplement "suspendue" jusqu'à ce que l'utilisateur se connecte à partir du modal. Je n'ai pas trouvé de moyen de mettre cela en œuvre.
Avez-vous essayé de changer votre bouton en utilisant tokenNotExpired
attribute comme dans cet exemple: https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and- poignée-routage
Il vous permet d'empêcher le 401 ...
Utilisez une session de navigateur:
https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage
Stockez les données sous forme de chaîne json, puis recréez les données du formulaire si la demande échoue.
Eh bien, le rechargement est simple: (<any>window).location.reload(true);
C'est une bonne idée d'afficher une fenêtre de connexion/mot de passe et de permettre à l'utilisateur de continuer à travailler s'il peut fournir un mot de passe, si l'utilisateur clique sur annuler, redirigez-le vers la page de connexion.
Il existe également des problèmes de connexion, des délais d'attente, des erreurs de serveur. Le problème est qu'il est difficile d'implémenter de manière fiable une gestion correcte en utilisant Angular 2 Http
module et rxjs
. Personnellement, j'ai abandonné l'utilisation de Http
module, car sa gestion des erreurs a une mauvaise conception et a implémenté mon propre module http, qui permet une gestion des erreurs configurable et des tentatives transparentes.