Angluar2 routing inside http interceptor

2019-04-19 01:25发布

I use Angular 2.4.8. Communication with backend is via REST. In each request I need to send X-Auth-Token in header. The token is stored on session. When token is outdated server returns 401 status. In such a case I want application to go to login page.

I added http interceptor to my project

@Injectable()
export class HttpInterceptor extends Http {

    constructor(backend: XHRBackend
        , defaultOptions: RequestOptions
        , private router: Router
    ) {
        super(backend, defaultOptions);
    }

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        return super.request(url, options).catch((error: Response) => {
            if ((error.status === 401 || error.status === 403) && 
            (window.location.href.match(/\?/g) || []).length < 2) {
                // tslint:disable-next-line:no-console
                console.log('The authentication session expires.');
                window.sessionStorage.removeItem('auth-token');
                window.location.href = window.location.href + '/login';
                // this.router.navigate(['/login']);
                return Observable.empty();
            }
            return Observable.throw(error);
        });
    }
}

and it works well except. But I don't use router but plain redirect and whole application reloads. When I changed commenting to

// window.location.href = window.location.href + '/login';
this.router.navigate(['/login']);

the app doesn't follow the link. How to make router to work (navigate)?

edit 2018-01-22

My app-routing.module.ts

const routes: Routes = [
    {
        path: 'login',
        component: LoginComponent,
        resolve: {
            boolean: InitResolverService
        }
    },
    {
        path: '**',
        redirectTo: 'system'
    }
];

@NgModule({
    imports: [
        RouterModule.forRoot(
            routes
            // , { enableTracing: true } // <-- debugging purposes only
        )
    ],
    exports: [
        RouterModule
    ]
})
export class AppRoutingModule { }

where in InitResolverService I have some logic to do on first navigation and then emit true and complete stream.

And LoginComponent

@Component({
    selector: 'app-login',
    templateUrl: 'login.component.html',
    styleUrls: ['login.component.less']
})
export class LoginComponent implements OnInit {
    private username: FormControl;
    private password: FormControl;
    public form: FormGroup;
    public displayDialog = false;
    isLoginButtonEnabled = true;
    isResetButtonVisible = false;

    constructor(
        private authService: AuthenticationService,
        private router: Router,
        private route: ActivatedRoute,
        private initService: InitResolverService
    ) {
        this.username = new FormControl(Validators.required);
        this.password = new FormControl(Validators.required);
        this.form = new FormGroup({
            Username: this.username,
            Password: this.password
        });
        this.form.setValue({
            Username: '',
            Password: ''
        });
        this.displayDialog = true;
    }

    ngOnInit() {
        this.initService.showSplash();
        this.authService.canActivate(this.route.snapshot, this.router.routerState.snapshot).subscribe(x => {
            if (x) {
                this.router.navigate(['/']);
            }
        });
    }
}

4条回答
叼着烟拽天下
2楼-- · 2019-04-19 01:33

at your app.module.ts you should add:

{provide : Http, useFactory: (xhrBackend: XHRBackend, requestOptions: RequestOptions, router: Router) => new HttpInterceptor(xhrBackend, requestOptions, router),deps: [XHRBackend, RequestOptions, Router]}

at your HttpInterceptor:

 constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private _router: Router) {
    super(backend, defaultOptions);
  }
查看更多
戒情不戒烟
3楼-- · 2019-04-19 01:50

I didn't use interceptor for that. with every API call I use .catch to catch errors and pass to this function:

  // define the error handler in a separate method so we can reuse it 
  //in other methods
  private handleError(error: HttpErrorResponse | any) {
    console.error('ApiService::handleError', error);
    if (error.status === 401) {
      this.oAuthService.logOut();
    }
   return Observable.throw(error);
  }

hope this helps.

查看更多
SAY GOODBYE
4楼-- · 2019-04-19 01:50

This code works for me:

@Injectable()
export class JwtInterceptor implements HttpInterceptor {

  constructor( public auth: AuthenticationService,
               private router: Router ) {
  } 

  public intercept( request: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> {
    let url: string = request.url;
    let method: string = request.method;
    console.log(`JwtInterceptor url=${url},   method=${method}`);

    return next.handle( request ).do( ( event: HttpEvent<any> ) => {
        console.log(`successful reply from the server for request=${request.urlWithParams}`);
    })
    .catch((responseError: any) => {

      // ResponseError Interceptor
      if (responseError instanceof HttpErrorResponse) {
        console.error('response in the catch: ', responseError);

          if ( responseError.status === 401 ) {
            let errorMsg: string = '';

            if ( responseError.statusText === 'Invalid credentials' ) {
              errorMsg = 'Username or password is incorrect';
            }

            // redirect to the login route
            this.router.navigate(['/login'], {queryParams: {msg: errorMsg}});
            return empty();
          }

        return throwError(responseError);
      }


      let error = new HttpErrorResponse({
        status: 500,
        statusText: 'Unknown Error',
        error: {
          message: 'Unknown Error'
        }
      });
      return throwError( error );

    }) as any;
  }
}
查看更多
乱世女痞
5楼-- · 2019-04-19 01:54

we are solve this case with write own custom http-service that we using all http request via REST.

Also you can with custom http-service;

  • central api path
  • create header with token
  • handle all HTTP err result even 401

Simple code sample

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';

export const API_PATH = "http://apipath"

@Injectable()
export class CustomHttpService {

    constructor(
        private http: Http,
        public router: Router) { }

    headerWithToken(): Headers {
        const headers = new Headers();
        headers.set('Authorization', 'bearer ' + localStorage.getItem('TOKEN'));
        headers.set('Content-Type', 'application/json');
        return headers;
    }

    get(params: URLSearchParams, method: string): Observable<any> {
        const url = `${API_PATH}/${method}`;
        return this.http.get(url, {params: params, headers: this.headerWithToken()})
        .map(
            res => <Array<any>>res.json()
        )
        .catch(err => {
            const result = this.handleErrors(err, this);
            return result;
        });
    }

    // put same way

    // post same way

    // delete same way

    public handleErrors(error: Response, obj: any): ErrorObservable {
        const errData = error.json();
        if (error.status === 401) {
            obj.router.navigate(['/login']);
        } else if (errData.message) {
            // give a message or something
        } else {
            console.log(errData);
        }
        return Observable.throw(error.json());
    }

}
查看更多
登录 后发表回答