Ionic3 Events事件失效:HTTP401时无法跳转登录页求助
Let's walk through why your unauthorised event isn't triggering the callback in app.component.ts—I've run into similar issues with Ionic's Events service before, so here are the most likely culprits and fixes:
1. You’re Using Multiple Instances of the Events Service
This is the #1 reason events fail to fire in Ionic/Angular. If your ServiceProvider, AuthService, or Events itself is registered in multiple places (e.g., both in app.module.ts providers and a component's providers array), you'll end up with separate instances. The instance publishing the event won't be the same one you're subscribed to.
Fix:
- Open
app.module.tsand confirm:Events,ServiceProvider, andAuthServiceare only listed once in theprovidersarray.- No component (like
MyApporLoginPage) has these services in its ownproviderssection.
- Add debug logs to verify instance consistency:
Inapp.component.tsconstructor:
Inconsole.log('App Events Instance:', this.events);ServiceProviderconstructor:
If the log outputs don't match (different object IDs), you have duplicate instances.console.log('Service Events Instance:', this.events);
2. Event Subscription Happens Before the Platform is Ready
Ionic's Events service relies on the underlying Cordova platform being fully initialized. If you subscribe in the MyApp constructor directly, the subscription might register before the service is ready to handle events.
Fix:
Move your event subscription inside platform.ready():
constructor( public platform: Platform, public menu: MenuController, public statusBar: StatusBar, public events: Events, public network: Network, public toast: Toast, public storage: LocalStorage ) { this.platform.ready().then(() => { this.statusBar.styleDefault(); // Subscribe to events after platform is ready console.log('Subscribing to unauthorised event'); events.subscribe('unauthorised', () => { console.log('user unauthorised take to login page'); this.storage.clear(); this.nav.setRoot('LoginPage'); }); }); }
3. The 401 Error Branch Isn’t Actually Executing
It’s easy to assume the 401 check is triggering, but let’s confirm:
Fix:
Add debug logs to your error handling flow:
In ServiceProvider.getErrorMessages():
getErrorMessages(errors) { console.log('Received error with status:', errors.status); // Verify status is 401 if (errors.status == 401) { console.log('Publishing unauthorised event'); // Confirm this line runs this.toast.present('You need to login first!'); this.events.publish('unauthorised'); } // ... rest of your code }
In AuthService.getAuthUser() error callback:
.subscribe(response => { resolve(response); this.events.publish('auth_user_fetched'); }, errors => { console.log('Auth API error details:', errors); // Confirm error is caught reject(this.getErrorMessages(errors)); });
If you don’t see these logs, the error isn’t reaching the 401 branch (e.g., the error response might have a different status code, or the error object structure isn’t what you expect).
4. AuthService Isn’t Properly Inheriting ServiceProvider’s Dependencies
If your AuthService doesn’t call super() with all the parent service’s dependencies, the Events instance in ServiceProvider could be undefined or incorrect.
Fix:
Ensure your AuthService constructor correctly passes all required dependencies to the parent:
import { Injectable } from '@angular/core'; import { Http, Headers, RequestOptions } from '@angular/http'; import { ServiceProvider } from './service-provider'; import { Toast } from '../utilities/toast'; import { Events } from 'ionic-angular'; import { LocalStorage } from '../utilities/localstorage'; @Injectable() export class AuthService extends ServiceProvider { constructor( http: Http, toast: Toast, events: Events, storage: LocalStorage ) { // Critical: Pass all parent dependencies to super() super(http, toast, events, storage); } // ... your getAuthUser() method }
Bonus: Better Alternative to Events for 401 Handling
Instead of relying on Events, consider using a custom HTTP interceptor (even in Ionic 3) to handle 401s globally. This avoids scattered error handling and event dependencies:
- Create a
CustomHttpclass that extends Angular’sHttp:
import { Injectable } from '@angular/core'; import { Http, Request, RequestOptionsArgs, Response, XHRBackend, RequestOptions } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/catch'; import 'rxjs/add/observable/throw'; import { Events } from 'ionic-angular'; @Injectable() export class CustomHttp extends Http { constructor(backend: XHRBackend, options: RequestOptions, private events: Events) { super(backend, options); } request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> { return super.request(url, options).catch((error: Response) => { if (error.status === 401) { this.events.publish('unauthorised'); } return Observable.throw(error); }); } }
- Replace the default
Httpprovider inapp.module.ts:
import { Http, XHRBackend, RequestOptions } from '@angular/http'; import { CustomHttp } from './custom-http'; import { Events } from 'ionic-angular'; export function customHttpFactory(backend: XHRBackend, options: RequestOptions, events: Events) { return new CustomHttp(backend, options, events); } @NgModule({ // ... other module config providers: [ { provide: Http, useFactory: customHttpFactory, deps: [XHRBackend, RequestOptions, Events] }, // ... other services ] })
Now every HTTP request will automatically trigger the unauthorised event on 401, no need to call getErrorMessages() in each service.
内容的提问来源于stack exchange,提问作者Abhishek

