I'm trying to implement an authentication scheme described here.
I'm struggling to find where the req parameter is defined in the code below. My code would not compile as it is not currently defined. This could be a typo in his code. I looked through the comments but nobody seems to have pointed that out:
// src/app/auth/jwt.interceptor.ts
// ...
import 'rxjs/add/operator/do';
export class JwtInterceptor implements HttpInterceptor {
constructor(public auth: AuthService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
// redirect to the login route
// or show a modal
}
}
});
}
}"
Can someone point out what i'm missing?
Many thanks in advance.
Looks like a typo to me. The intercept function provides a parameter request - it should probably be referring to that instead of req.
The parameter must read as request as below
return next.handle(request)
.do(event => {
if ()
Related
I am trying to make an interceptor to refresh the token, but it throws me this error and I don't know why
ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
token-interceptor.service.ts
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { HttpClient, HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { environment } from 'src/environments/environment';
import { catchError, map} from 'rxjs/operators';
import { throwError } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class TokenInterceptorService implements HttpInterceptor {
constructor(
private auth: AuthService,
private http: HttpClient
) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
return next.handle(req).pipe(
catchError((err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.url.includes('signin') || err.url.includes('refreshToken')) {
return next.handle(req)
}
//if error is not about authorization
if (err.status !== 401) {
return next.handle(req)
}
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
} else {
return throwError(err)
}
})
)
}
renewToken(req: HttpRequest<any>) {
return this.http.get(`${environment.API_URL}/refreshToken`, { withCredentials: true }).pipe(
map((res: any) => {
//update access token
this.auth.setToken(res.token)
return req.clone({
setHeaders: {
authorization: `Bearer ${res.token}`
}
})
})
)
}
}
Ignore this: It looks like your post is mostly code; please add some more details. It looks like your post is mostly code; please add some more details.
this piece of code is wrong:
this.renewToken(req).subscribe(request => {
return next.handle(request)
})
istead it should be:
return this.renewToken(req).pipe(switchMap(request => next.handle(request)));
you are just returning nothing in your variant, that is why it doesn't work.
also the whole logic of token interpceptor seems weird to me. I believe you should rethink about how you want it to work. for now as I see you sending request without token and in almost all cases you are sending it again unmodified, and the one that I fixed above will send it again with token. Wouldn't it be right to add token every time, and only send it 2nd time if token is outdated?
I'm confused about general good practice when it comes to error handling. For example, if I'm already catching the error in my service, do I still need to include the error handler in my subscription?
Here's my http method in my service. As you can see it calls catchError:
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`)
.pipe(
catchError(this.handleError)
);
}
private handleError(res: HttpErrorResponse | any) {
console.error(res.error || res.body.error);
return observableThrowError(res.error || 'Server error');
}
And in my component:
delete(id: number){
this.deleteService.deleteTask(id).subscribe(
(val) => {
/*post processing functionality not relevant to this question
*/
}
);
}
In the angular documentation https://angular.io/guide/observables the error handler is described as optional:
myObservable.subscribe(
x => console.log('Observer got a next value: ' + x),
err => console.error('Observer got an error: ' + err),
() => console.log('Observer got a complete notification')
);
So in my example, would including the error handler on my subscription add anything to my code? Like if I did:
delete(id: number){
this.deleteService.deleteTask(id).subscribe(
(val) => {
/*post processing functionality not relevant to this question
*/
},
err => console.error('Observer got an error: ' + err)
);
Would it catch anything my catchError didn't catch? It almost feels like it would be good practice to always include the error handler, so I don't know why it's marked as optional? When should one use the subscription error handler vs other forms of error handling?
It's all about how you want to handle error in your application,
if you want to throw a fancy error instead of the actual error comming back from server, you can have a error handler in service end and who ever consume your service will get the fancy error instead of actual error.
// copied from your question
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`)
.pipe(
catchError(this.handleError)
);
}
private handleError(res: HttpErrorResponse | any) {
console.error(res.error || res.body.error);
return observableThrowError(res.error || 'Server error');
}
if not, you dont need to handle error in your service code, let the consumer(service/component) handle it.
deleteTask(id: number): Observable<any>{
return this.http.delete(this.tasksUrl+'/'+`${id}`);
}
// component
...
this.service.deleteTask(id).subscribe(success,(err) => {
// example
alert(err.message);
});
...
To handle common http errors (500, 401, 403, 404) you can write a HttpInterceptor, so that you dont need to write the error handling logic everywhere.
import { Injectable } from '#angular/core';
import {
HttpEvent, HttpRequest, HttpHandler, HttpInterceptor, HttpErrorResponse
} from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
#Injectable()
export class MyAppHttpInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
// redirect to login page
} else {
return throwError(error);
}
})
);
}
}
if you want to log the errors to server or if you want to show a custom console or error notification over the screen on development mode or for debugging, you can create a global error handler service extending the existing ErrorHandler servcie in angular.
import { ErrorHandler } from '#angular/core';
#Injectable()
export class GlobalErrorHandler implements ErrorHandler {
handleError(error) {
// your custom error handling logic
}
}
Would it catch anything my catchError didn't catch?
No, you are just passing the same error through. But if your subscription doesn't have an error handler you will get an exception if you don't handle it. So you should either have an error handler on your subscription or pass an observable with no data.
const { throwError, of } = rxjs;
const { catchError } = rxjs.operators;
throwError('error').pipe(catchError(error => {
console.log('Caught error - ', error);
return of(null);
})).subscribe(val => { console.log('No error handler needed'); });
throwError('error').pipe(catchError(e => {
console.log('Caught error - ', e);
return throwError(e);
})).subscribe(val => {}, error => { console.log('Subscription handled error - ', error); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
I need to increase the request time of the angular app because using slow internet connections timeout happens.
I tried the code below and had an error.
this.http.post(url, body, { headers: headers })
.timeout(100, this.handleTimeout)
.map(response =>{
return response;
})
.catch(this.handleErrors);
Property 'timeout' does not exist on type
'Observable'.ts(2339)
Not success using interceptor too
#Injectable()
export class AngularInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).timeout(5000);
}
Property 'timeout' does not exist on type
'Observable>'.ts(2339)
Thanks
The final solutions that works for me:
import { timeout} from 'rxjs/operators';
return this.http.get(`${url}`).pipe(
timeout(1000)
);
Thanks to all for the help.
With Rxjs 6, you will have to use a .pipe and then use an operator like .timeout
So your implementation should look like:
import {
timeout,
map,
catch
} from 'rxjs/operators';
this.http.post(url, body, { headers: headers })
.pipe(
timeout(100, this.handleTimeout),
map(response =>{
return response;
}),
catch(this.handleErrors);
I am trying to write unit test for this angular script:
export class DataService {
private csrfToken: string = '';
private isContentShow: BehaviorSubject<boolean> = new BehaviorSubject(true);
constructor(private http: HttpClient, private cookieService: CookieService) {
this.token = this.cookieService.get('token');
}
public createData(data: Data) {
try {
this.http.post( url,
data,
{
headers: new HttpHeaders({
'Content-Type': 'application/json',
'Authorization': this.token
})
})
.subscribe(
data => {
this.isContentShow.next(true);
},
err => {
this.showError();
},
() => console.log('Request Complete')
);
return true;
} catch {
this.showError();
}
}
public getIsContentShow(): Observable<boolean> {
return this.isContentShow.asObservable();
}
}
The test that I had so far and its running as expected.
it('#getIsContentShow should return value from observable',
(done: DoneFn) => {
service.getIsContentShow().subscribe(value => {
expect(value).toBe(true);
done();
});
});
However I am trying to write the test for createData() function
I am able to mock the HttpClient using HttpClientTestingModule however I don't know how to handdle the CookieService and token ?
Thanks
You can use spies to spy on the cookieService get method. This way, you can write your unit test to test the combinations of returns you say the cookieService can provide.
This link says that you can spy on the prototype of the method in order to handle it how you like in the constructor.
it(
"should call #getGeneralStats in the constructor",
inject(
[CookieService, HttpClient],
(cookieService: CookieService, http: HttpClient) => {
let mySpy = spyOn(cookieService, 'get').and.returnValue(<your value>);
dataService = new DataService(http, cookieService);
expect(mySpy).toHaveBeenCalled();
}
)
);
For you, this may depend on how you're writing your tests. The example shows the service being instantiated like new ServiceName, but it's also possible to use dependency injection to get the service. If you're using DI for the service you are testing, I'd have to research more how to do this (others please feel free to add your answer if you know how to do that)!
Problem statment
I am very new to Angular 4 and struggling to find out how to get a user re-logging when the token expires.
Lets dig into code
I have an response intercepter that checks the response code for 401 error
intercept(request: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(request).do(
// success responses
(event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// I do not want to do anything here... just pass
}
},
// error responses
(err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
//here is where I need to show a modal
//OH! STACKOVER-FLOW PLEASE BLESS ME
}
}
});
}
Just Informing
The application is too modular as every component is a module itself. Like for an example : Login Component is a module itself and Registration is Another module which are included in a the root module using routes...
So could you please help me with the best practice to solve this riddle?
I'm using Angular4 CanActivate to check whether user logged in or not, I think it would works the same way as your approach.
Anyway in your canActivate service or inside of your hook I can see 2 solutions:
1) as #Sajal mentioned - broadcast event:
#Injectable()
export class YourService {
heyStopRightThere: EventEmitter<boolean> = new EventEmitter();
intercept(request: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
...
if (err.status === 401) {
this.heyStopRightThere.emit();
}
...
}
}
and then in all secured components
constructor(
private _yrSvc: YourService
) {
}
showLoginDialog() {
//enable component LoginDialog that embeded in
// <loginDialog *ngIf="notLoggedIn"></loginDialog>
}
ngOnInit() {
this._yrSvc.heyStopRightThere.subscribe(() =>
showLoginDialog()
);
}
2) Redirect with param to callback:
#Injectable()
export class YourService {
constructor(private router: Router){}
intercept(request: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
...
if (err.status === 401) {
this.router.navigate(['/login', {callback: location.href}]);
}
...
}
And then your Login component you can draw your dialog and redirect back to "callback" on success.