-
Notifications
You must be signed in to change notification settings - Fork 24.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cyclic dependency error with HttpInterceptor #18224
Comments
Will it work like this? @Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(@Inject(forwardRef(() => AuthService)) private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler) {
const authHeader = this.auth.getAuthorizationHeader();
const authReq = req.clone({headers: req.headers.set('Authorization', authHeader)});
return next.handle(authReq);
}
} |
@tytskyi I tried that and didn't work
works though |
I confirm that @tytskyi solution doesn't work. |
For info, for now I've separated in two services, one for login/signin/... (which uses HttpClient) and one just for managing the AuthToken (no Http). But going further, I added a error handler to catch 401 errors (when the token is invalid). But in such case, I need to call logout, so I need the service with HttpClient, and thus I fall in the initial problem again, so it is very problematic. |
This is a true cyclic dependency, and @Toxicable's example is the correct way to handle it. However, I'd be concerned if the |
No, the |
My code is very similar to @cyrilletuzi, I tried @Toxicable solution, I have no more cyclic dependency but I get:
|
I use HttpClient and I create this interceptor to add the jwt token. Everyting work perfect but I have a bad practise. I use Http inside the HttpClient Interceptor. If I change
Cannot instantiate cyclic dependency! InjectionToken_HTTP_INTERCEPTORS ("[ERROR ->]")
Another important thing for those who will use this interceptor for their project but irrelevant to the current problem is to set headers to the refresh token response at least some seconds. ->header('Cache-Control', 'public, max-age=45') |
This is interesting, I thought that having service like AuthService, which manages token accessibility and performs login and refresh requests it's a common use case. So basically here is the scenario. We have AuthService which depends on HttpClient (obviously). It has few methods: getAccessToken, getRefreshToken, login, refreshSession and logout. Also in addition we need to have AuthInterceptor, which will add token to the request, if token exists. And obviously it depends on AuthService, cause service has access to the token. So here's the problem: HttpClient depends on HTTP_INTERCEPTORS (basically it's AuthInterceptor), AuthInterceptor depends on AuthService, and AuthService depends on HttpClient. And Cyclic error. So this common use case is not possible in the current implementation. But why angular team couldn't implement interceptors in the similar way how the first angular interceptors work? E.g. to have extra store service, using which one we can register new interceptors, e.g. I wrote similar library called ng4-http, which implements similar approach to the first angular. It uses additional service which stores registered interceptors. And in this case we can inject any kind of services there and use them properly. Current library uses old http module. So the question is, am I wrong? If yes, then where ? |
I have code that is quite similar to the examples posted here, and I've been encountering the same issue. In fact, it seems extremely close to what @cyrilletuzi is trying to do, I'm just trying to get the value of a token property I have set in an AuthService, I'm not trying to make use of it any other way. Just access that in the interceptor. Adding the service to the constructor in the interceptor gives me the cyclic dependency error, basically what @mixalistzikas got, down to the message, while trying the solution @Toxicable provided seems to give me the same issue @perusopersonale got, i.e. a problem with too much recursion, and the app crashing. Eventually just decided to access the token directly from session storage where I had it stored, rather than retrieve it from the service, but the fact that this issue seems to be occurring when there doesn't seem to be any real cyclic dependency seems off. The workaround I described works for now for me, but definitely many situations where you would need to access a service somewhere, and that doesn't seem possible in this manner |
This example also works for me, but i worred because http in the future may be deprecated and also is not good for the app to inject 2 different libraries for the same thing. If anyone find a solution, please answer... |
As a temporary solution Auth token can be stored as a static prop on the AuthService class. Then you won't need to instantiate it. |
You may also try a little hack to instatiate the service, like:
This way you won't get the max call stack exceeded error. |
I resolved simply not setting authService in constructor but getting in the intercept function.
|
That's perfect @perusopersonale ..... You solve my problem... |
That's the final interceptor with some logging functionality and keep loading status for loader animation
And don't forget... Another important thing for those who will use this interceptor for their project is to set headers to the refresh token response at least some seconds. ->header('Cache-Control', 'public, max-age=45') Also...
|
Just to remind if someone does not know. RefreshToken can be retrieved only while main token is not expired. You can call token endpoint and post refresh token to obtain new accessToken (e.g. 1 hour before main token os expired).The problem is the following. In such case our method getAccessToken() will return observable result because it exposes http end obtaines new token using refresh token if necessary. |
@perusopersonale that stack overflow error means that your circular dependency has been resolved at build time but is still very much present at runtime, you're making a new request from within the interceptor which also goes through the interceptor, and that repeats forever. I suggest using |
I want to inject a service for the purpose of getting a value stored out of the service, not to make a request, but that service does make use of the HttpClient to make requests. I don't think this issue should be closed. |
@nazarkryp when using the manual injection method, I seem to be getting a different instance of the service. That could be my fault with how I set up the provider for the service (although I think I took care of only providing it once). Are you seeing the same thing? |
I'm having the same issue, and seems there's still no satisfying answer provided above. This shouldn't be closed. |
Agreed @will-copperleaf. I still view this as a design problem with |
Getting an Error:
AuthHttp class:
|
@will-copperleaf Which example have you tried? You should be able to do this for example: import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { AuthService } from '../shared/auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private authService: AuthService;
constructor(
private injector: Injector
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.authService = this.injector.get(AuthService); // get it here within intercept
const authRequest = request.clone({
headers: request.headers.set('Authorization', this.authService.getHeaders())
});
return next.handle(authRequest);
}
} |
So basically if my authService needs to do an http request to retrieve both the token and the refresh token and then store those in localStorage, how would you propose to implement this? From what I understand, there is no way to exclude specific requests from the interceptors so that they don't get intercepted, there is no way to load the interceptors for a specific component or module (they have to be defined in the app module). |
I use this solution in my
|
@MrCroft I just updated to angular 5.2.3, it is working just fine. |
I also get the cyclic dependency error when I try to build with the --prod flag. |
I was getting the same cyclic error when using angular 5.2.2. |
@jzahoor I'd like to know more about the fix. |
@melicerte sorry for the late response, didn't realize I had to pay my hosting provider. It was weird not getting email notifications for a few days, until I realized why :) On topic: It turns out, if you have a Universal app, you still get the error. Of course I didn't notice this until I decided to create a new app, from scratch 😄 @alxhub I've made a repo here to reproduce it. Angular CLI: 1.6.8 |
@marcincichocki: interested to know how |
@jdhines In that particular example you would need to provide @NgModule({
providers: [
{
provide: MY_REQUEST,
useValue: new HttpRequest() // set your HttpRequest here, or use factory function if you need DI
}
]
}) However this is good if you need highly portable and reusable code(let's say you have multiple apps that use different backends). I you don't, just set @Injectable()
export class MyInterceptor {
myRequest = new HttpRequest()
} |
@marcincichocki: thanks (and apologies for the thread here). Pardon what's probably a basic question; this is all pretty foreign to me, but is |
@jdhines Yes, you must pass arguments describing your request. That class have few overloads angular/packages/common/http/src/request.ts Lines 129 to 164 in c8a1a14
Most commonly you would use something like this: const myRequest = new HttpRequest('POST', 'url/here', payload, options) // post
const myRequest2 = new HttpRequest('GET', 'url/here', options) // get |
Previously, an interceptor attempting to inject HttpClient directly would receive a circular dependency error, as HttpClient was constructed via a factory which injected the interceptor instances. Users want to inject HttpClient into interceptors to make supporting requests (ex: to retrieve an authentication token). Currently this is only possible by injecting the Injector and using it to resolve HttpClient at request time. Either HttpClient or the user has to deal specially with the circular dependency. This change moves that responsibility into HttpClient itself. By utilizing a new class HttpInterceptingHandler which lazily loads the set of interceptors at request time, it's possible to inject HttpClient directly into interceptors as construction of HttpClient no longer requires the interceptor chain to be constructed. Fixes angular#18224. PR Close angular#19809
If you have this problem on Ionic, you might find yourself struggling with Maximum Call Stack Size Exceeded. Just do the inj.get(...) inside platform.ready().then(() => [...]); inside of the Interceptor! |
I got stuck with a similar cyclic dependency and resorted to do the workaround of manually injecting the service https://stackoverflow.com/questions/49240232/getting-a-cyclic-dependency-error |
I still have this problem, anyone has soved anyway? The error: ERROR Error: Uncaught (in promise): RangeError: Maximum call stack size exceeded My code:
|
@thiagodamico I now have the exact same error message as you and I don't think I have touched anything in my source code. |
Me too. |
Previously, an interceptor attempting to inject HttpClient directly would receive a circular dependency error, as HttpClient was constructed via a factory which injected the interceptor instances. Users want to inject HttpClient into interceptors to make supporting requests (ex: to retrieve an authentication token). Currently this is only possible by injecting the Injector and using it to resolve HttpClient at request time. Either HttpClient or the user has to deal specially with the circular dependency. This change moves that responsibility into HttpClient itself. By utilizing a new class HttpInterceptingHandler which lazily loads the set of interceptors at request time, it's possible to inject HttpClient directly into interceptors as construction of HttpClient no longer requires the interceptor chain to be constructed. Fixes angular#18224. PR Close angular#19809
I'm not sure why noone else has pointed this out but apparently this is fixed for Angular 6 by means of introducing an |
Any example for HttpInterceptingHandler usage? |
@ronen1malka You are not supposed to use this class as it is not part of the public API. Simply upgrade to the 6.0.2 version and keep using the HttpInterceptor class. |
@simeyla pointed out about HttpInterceptorHandler, good point! |
the bad part is something not working upgrade it !! lot of dirt being pushed on NPM |
Works with ionic 3 and angular 5 |
got same issue on angular 6 but solved with this method #18224 (comment) |
@Toxicable thank you, your solution works for me! I got the same issue under angular 5.0.x, but it seems that the issue is fixed under angular 5.2.11. @Toxicable 's sulotion works under both 5.0.x and 5.2.11 |
This also works: import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { AuthService } from '../auth/auth.service';
@Injectable({
providedIn: 'root'
})
export class JwtInterceptorService implements HttpInterceptor {
constructor(private authService: AuthService) {
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Add authorization header with bearer token if available.
const token: string = this.authService.tokenValue;
if (token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
}
return next.handle(request).pipe(catchError((error: HttpErrorResponse) => {
// Auto logout if 401 response returned from api
if (error.status === 401) {
this.authService.unAuthUser();
}
return throwError(error);
}));
}
} |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
I'm submitting a...
Current behavior
Trying to do the same example of AuthInterceptor like in official doc, to add a Authorization header on all requests.
Problem : with real code, the
AuthService
needs to request the server on login, signin, etc., so it itself injectHttpClient
.It creates this fatal error in console :
Expected behavior
Should work, as it's a very common case. Otherwise interceptors are limited to use services without Http.
Environment
The text was updated successfully, but these errors were encountered: