Unable to handle HTTP/404 when calling httpClient.delete() in Angular 8

Viewed 3423

I have Springboot 2.1 endpoint that returns HTTP/404 when calling DELETE method. There is Angular 8 application that calls that endpoint using HttpClient. It has been stripped down to simplest possible code.

If I call httpClient.delete('url').subscribe(); while backend returns HTTP/404 I'm unable to handle errors. Angular throws this error in console: ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

I tried handling errors as it's written in Angular http client description but it does not help. Before any error handling code is called, Angular throws error in console and no error handling is executed.

Here's the Java code

@RestController
@RequestMapping("/api/test")
@RequiredArgsConstructor
class TestController {

  @DeleteMapping("/{id}")
  public ResponseEntity deleteObject(@PathVariable("id") Long id) {
    return ResponseEntity.notFound().build();
  }
}

Angular code calling this endpoint is:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class ObjectsService {

  constructor(private httpClient: HttpClient) {
  }

  deleteObject() {
    return this.httpClient.delete('http://localhost:8090/api/test/5').subscribe();
  }
}

Chrome shows that the response is:

Request URL: http://localhost:8090/api/test/5
Request Method: DELETE
Status Code: 404 
Remote Address: [::1]:8090
Referrer Policy: no-referrer-when-downgrade

Unfortunately instead of normal execution I'm getting an error in the console:

ERROR TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
    at subscribeTo (subscribeTo.js:27)
    at subscribeToResult (subscribeToResult.js:11)
    at CatchSubscriber.error (catchError.js:38)
    at XMLHttpRequest.onLoad (http.js:2476)
    at ZoneDelegate.invokeTask (zone-evergreen.js:391)
    at Object.onInvokeTask (core.js:39679)
    at ZoneDelegate.invokeTask (zone-evergreen.js:390)
    at Zone.runTask (zone-evergreen.js:168)
    at ZoneTask.invokeTask [as invoke] (zone-evergreen.js:465)
    at invokeTask (zone-evergreen.js:1603)

edit: There is also HTTPInterceptor that was handling errors:

  private handleErrors(err: HttpErrorResponse): Observable<any> {
    if (err.status === 401) {
      this.loginService.redirectToUrl = this.router.url;
      this.router.navigate(['/login']);
      return of(err.message);
    }
  }

Thanks to @igor, "missing else" was spotted. Adding HTTP/404 handling in interceptor solved this problem.

4 Answers

If the error handling code executing on the observable is not being reached then you have an HttpInterceptor configured which is handling the error before this code is reached.

You need to pass argument in subscribe() that will be response. In your example

deleteObject() {
  return this.httpClient.delete('http://localhost:8090/api/test/5').subscribe(response => {
    // Here you will handle response
  }, (error) => {
    // Here you can handle errors
  });
}

A good start I would say is to implement interceptor and handle those errors:

@Injectable()
export class ServiceHttpInterceptor implements HttpInterceptor {

 intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
   ...........
   next.handle(req)
            .pipe(tap(event => {
                if (event instanceof HttpResponse) {
                  // doNothing now
                }
            }))
            .pipe(catchError(err => {
                      if (err instanceof HttpErrorResponse) {
                          // Handle your 404 here
                          }
           }
}

}
Related