I would like error responses, in the response body, from my API server to flow all the way to my component for certain error codes, for example 400 or 422. It works well without an HttpInterceptor in the following implementation.
function handleHttpError() {
return (response: any) => {
let newResponse;
if (response.status === 0) {
// Error connecting to the server
newResponse = { error: 'Error connecting to the server!' }
} else {
// Server returns a response in response.error
newResponse = response.error;
}
return of(newResponse);
};
}
export class MyService {
...
saveTransaction(trans: Transaction): Observable<APIResponseBody> {
return this.http.post<APIResponseBody>(this.transUrl, trans)
.pipe(catchError(handleHttpError()));
}
...
}
export class MyComponent {
...
onSubmit() {
this.myService.saveTransaction(this.data) {
.subscribe((res: APIResponseBody) => {
if (res.error) {
this.toastService.showError(`${res.error}: ${JSON.stringify(res.attributes)}`);
} else if (res.trans) {
this.trans = res.trans;
}
})
}
}
...
}
It does not, however, work when I move the error handling to HttpInterceptor. All non-2xx http errors seem to be filtered by the interceptor in the following implementation:
export class HttpInterceptorService implements HttpInterceptor {
constructor() { }
intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(
catchError((response: any) => {
let newResponse;
if (response.status === 0) {
// Error connecting to the server
newResponse = { error: 'Error connecting to the server!' }
} else {
// Server returns a response in response.error
newResponse = response.error;
}
return of(newResponse);
})
);
}
}
}
export class MyService {
...
saveTransaction(trans: Transaction): Observable<APIResponseBody> {
return this.http.post<APIResponseBody>(this.transUrl, trans);
}
...
}
What am I missing in my interceptor implementation?
CodePudding user response:
I think this is mostly because of not returning the correct type in your handler function, you're returning an observable of unknown type which is not the interceptor expects. Take a look at this and try it:
@Injectable()
export class ErrorHandlerInterceptor implements HttpInterceptor {
intercept(request: HttpRequest < any > , next: HttpHandler): Observable < HttpEvent < any >> {
return next.handle(request).pipe(catchError((error) => this.errorHandler(error)));
}
// Customize the default error handler here if needed
private errorHandler(response: HttpErrorResponse): Observable < never > {
if (!environment.production) {
// Do something with the error
this.logger.logError('Request error ' JSON.stringify(response));
}
// console.error(error);
const httpErrorCode = response.status;
switch (httpErrorCode) {
case 404:
this.router.navigateByUrl('/not-found');
break;
case 403:
this.router.navigateByUrl('/forbidden');
break;
case 500:
default:
// 500 Error Handing
break;
}
throw response;
}
}
CodePudding user response:
The interceptor needs to return an Observable<HttpEvent>
, in your case an Observable<HttpResponse>
So instead of returning the newResponse
object directly you just need create a new HttpResponse
and set your response as the body of the response.
export class HttpInterceptorService implements HttpInterceptor {
constructor() { }
intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(
catchError((response: any) => {
...
return of(new HttpResponse({ body: newResponse }));
})
);
}
}
cheers
CodePudding user response:
You mention 2 examples here so I would try to explain whats happening in each one.
1st example without the interceptor
I see problems with the way that you use the catchError
function.
saveTransaction(trans: Transaction): Observable<APIResponseBody> {
return this.http.post<APIResponseBody>(this.transUrl, trans)
.pipe(catchError(handleHttpError())); <-------------------------
}
Inside catch error you either pass an arrow function () => { }
of how it should behave or a reference to an existing function in your case handleHttpError
.
So the correct way would be
saveTransaction(trans: Transaction): Observable<APIResponseBody> {
return this.http.post<APIResponseBody>(this.transUrl, trans)
.pipe(catchError(handleHttpError)); <-------------------------
}
The way that you have it as handleHttpError()
is a direct execution of the method handleHttpError and it is then passed to the catchError the response of that invocation. Also the function handleHttpError
you have declared does not take any parameter which is wrong.
So the first example without the interceptor is wrong and is just a coincidence that what you see matches with what you expect.
2nd example with interceptor
Let's go to the second example with the interceptor. There the arrow function that you pass in catchError is not correctly implemented and therefore it does not work.
Try it with the following
catchError((response: HttpErrorResponse) => {
let newResponse;
if (response.error instanceof ErrorEvent) {
newResponse = { error: 'Error connecting to the server!' }
} else {
//The response object here holds 2 information
// response.error -> information about error
// response.status -> information about the status code returned
newResponse = response;
}
return of(newResponse);
});