Search code examples
angulartypescripthttpclient

Endpoint returns an error, but http subscription doesnt catch it


I have a simple piece of code.

let body = new FormData();
body.set("a", true);
body.set("username", this.user);
body.set("password", this.password);

let headers = new HttpHeaders();
headers.set("Content-Type", 'application/x-www-form-urlencoded')

console.log("about to post");
this.http.post("/endpoint", body, {headers: headers}).subscribe(
    (result) => { console.log("success", result); /*extra code*/},
    (error) => { console.log("error", error); /*extra code*/ },
    () => { console.log("Complete"); /*extra code*/ });

This is a pretty simple subscription.

So i do a post request, and it throws a 401 error network request. The issue I have noticed is that the console will say

about to post
POST [url] 401 (Unauthorized)
Complete

It doesnt carry out the error functionality.

in the constructor http is defined as: private http: HttpClient

So i am a bit torn in understanding as to why it fails to throw the error. Is there something which is failing to work? I was looking at similar posts, such as Catching errors in Angular HttpClient which I thought i was following in regards to a subscription.

On a successful request, it calls success just fine.

Edit I was looking at the code more, and figured that maybe the data or the headers were wrong. So i am going to update them above. It seemed that the data being passed up looked off, so i updated it to be form data. I walked through the endpoint until it hits a throw new Exception("Authorization Error"), but it never calls the error function. Maybe there is something i am missing in the definition of the post request? Maybe something breaks internally because I failed to set something correctly?


Solution

  • Here I have prepared a mock so you can play

    Basically it mocks a server call intercepting some http requests with 'FakeBackendInterceptor' component. There I simulate an unauthorized response if you post a request to '/protected' endpoint.

    The only component to start the application is HelloComponent which calls the get and post service to mimic both authorized and unauthorized calls.

    In the end you were right because it is capturing as an error. Fork it so you can try anything else. I am used to intercept all errors (or unauthorized calls) in one place, I've just added an ErrorInterceptor component so you can try both approaches.

    Pay attention to the declaration at app.module (order matters):

    providers:    [ 
        { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: FakeBackendInterceptor, multi: true },
      ], 
    

    don't forget to add HttpClientModule to imports:

    imports:      [ HttpClientModule, BrowserModule, FormsModule ],
    

    If you don't want to use the interceptor:

      this.service.post().subscribe(
        (result) => { console.log("success", result); /*extra code*/},
        (error) => { 
          console.log("error", error); /*extra code*/ 
          if (error.status === 401)  {
            console.log("You are not authorized");
          }
        },
        () => { console.log("Complete"); /*extra code*/ })
    }
    

    I've added yet another extra case so you can deal with 401 in the service itself. If you send a PUT request you can test it:

      let body = new FormData();
      body.set("a", 'true');
      body.set("username", 'user');
      body.set("password", 'password');
    
      let headers = this.createHeaders();
      
      console.log("calling services unauthorized PUT");
      return this.http.put<any>("/protected", body, { headers, observe: "response" }).
      pipe(
        map(res => {
        if (res.status === 400 || res.status === 401) {
          throw new Error(res.body.message);
        }
        return res.body;
      }));