5

I'm sending a request to a service which requires authentication and since my current state isn't, I'm getting a 401 response. I'm trying to see if I can handle this in my code, at client side and written in Typescript:

this.http.post(url, body, options).catch(e => {
    if (e.status === 401) {
        //Handle 401
    }
    return Observable.throw(e);
});

But the problem is that e.status is zero even though from the network panel I can see that the response's status is actually 401. Is this a problem with Angular or did I do something wrong?

This is the return value for JSON.strigify(e):

{
    "_body": { "isTrusted": true },
    "status": 0,
    "ok": false,
    "statusText": "",
    "headers": {},
    "type": 3,
    "url": null
}

BTW, I'm using Angular 4.0.0 (core and http).

This is the error I'm getting in the console due to the post request sent above:

(2) POST http://192.168.2.10:8080/test 401 ()
XMLHttpRequest cannot load http://192.168.2.10:8080/test. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://192.168.2.10:4200' is therefore not allowed access. The response had HTTP status code 401.

One other mystery is that there are 2 post request errors reported on the console while there's only one sent according to network panel.

Mehran
  • 15,593
  • 27
  • 122
  • 221
  • What's the reason to catch and then rethrow the error? Why dont you subscribe with an onError? You should use it to restart the sequence unless you want to check the value for some side effect. In that case better use a .do operator – LookForAngular Jun 26 '17 at 17:29
  • Are you getting a CORS error? – Kevin B Jun 26 '17 at 17:30
  • @LookForAngular this code is just to see if I can detect 401. Once I did, I would return something else. – Mehran Jun 26 '17 at 17:37
  • @KevinB No. It's a simple 401 status code which as I explain is completely expected. I'm just asking why I can not see the error code in the `.catch`!? – Mehran Jun 26 '17 at 17:39
  • I asked because in most cases you can see the status code, but there are a few exceptions, cors errors being one of them. another case would be it failing for other reasons before or after the request is sent/completed, such as in an interceptor. – Kevin B Jun 26 '17 at 17:43
  • @LookForAngular First of all, I'm not an expert on Agular and may be what I'm going to say is because of my lack of knowledge. But I'm using `.catch` because I'm trying to act on the error in a service which handles server communication while it's the service caller who calls the `.subscribe`. If you think my comment does not make any sense, please provide your solution as an answer (with a little more depth) so I can test it. Thanks. – Mehran Jun 26 '17 at 17:43
  • @KevinB As you can see from the question, there's no indication in the returned error object why the request has failed. I'm trying to distinguish between "unauthorized" access and other server errors that might happen. So far I can not do so. – Mehran Jun 26 '17 at 17:45
  • @KevinB same error here, very hard to code like on expressjs, angular2, 4, always very hard for simple things, just get the code on response header, an impossible thing ? – stackdave Jun 26 '17 at 20:29
  • It's likely some other error is occuring, not network related but logic/code related. – Kevin B Jun 26 '17 at 20:30
  • @KevinB In that case, it's some code error in RxJs/Angular because I don't have any code between sending the request and the `.catch` handler depicted above. Also, one other factor, I'm serving the service on a different address than the frontend and thus the browser sees that as a CORS and because of that it sends requests in pairs. All the requests are escorted with an OPTIONS request. But again, in this case, the OPTIONS request is passing with 200 and my POST is facing 401 while I'm getting a 0! – Mehran Jun 26 '17 at 21:55
  • From what you've specified, i'd expect you to get a status code. but the fact that you aren't seems to indicate that somewhere along the line, between receiving that 401 response and your success/error handlers, the response is being intercepted and changed. Not a whole lot i can do to help past that, unless your console contained a CORS error. – Kevin B Jun 26 '17 at 22:06
  • I'll update the question with the error I'm getting on the console, thanks. – Mehran Jun 26 '17 at 22:24
  • @KevinB I guess I was wrong to say there's no CORS error! Which amazes me, I was not expecting any. Could you please tell me why I'm getting CORS error? – Mehran Jun 26 '17 at 22:34
  • not really, other than the 401 error response doesn't have the specified header. i love cors errors, they're always so precise in specifying what is missing. – Kevin B Jun 26 '17 at 22:36
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/147661/discussion-between-mehran-and-kevin-b). – Mehran Jun 26 '17 at 22:48

3 Answers3

1

I take too long to figure out that problem: you need add OPTIONS in the CORS method configuration in the server side. My final CORS configuration to express is:

const corsOpt = { 
    origin: 'https://www.yourhost.com', // to configure origin url in the server 
    methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'], // to work well with web app, OPTIONS is required 
    allowedHeaders: ['Content-Type', 'Authorization'] // allow json and token in the headers 
}; 
Ângelo Polotto
  • 8,463
  • 2
  • 36
  • 37
0

If you want to know the status of error of your service

 import { Injectable } from '@angular/core';
 import { Http, Response, Headers } from '@angular/http';
 import 'rxjs/add/operator/map';
 import { Observable } from 'rxjs/Observable';
 import 'rxjs/add/observable/throw';
 import 'rxjs/add/operator/catch';



 @Injectable()
 export class ExampleService {

    headers: Headers;

    constructor(private http: Http) {
            this.headers = new Headers();
            this.headers.append('Content-Type', 'application/json; charset=utf-8');
            this.headers.append('Accepts', '*/*');
            // this.headers.append()
    }

  service1() {

     let url = 'http://example.com/products';
     return this.http.get(url, this.headers).map((resp: Response) => { 
                    console.log('resp status', resp.status);
                    return resp.json();
            }).catch((resp: Response) => {
                    let details = resp.json();
                    console.log(resp.status); // here you get the error code in your case 401
                    return Observable.throw(details);
            });
  }
}

in the example the verb http is GET you can to try with POST, it is the same idea.

I hope that it helps you

alehn96
  • 1,363
  • 1
  • 13
  • 23
  • what you need, know the status code of error in http response? – alehn96 Jun 26 '17 at 20:39
  • it's working now , // .catch(this.handleError); .catch ((res: Response) => this.handleError(res); – stackdave Jun 26 '17 at 20:40
  • why some examples use only .catch(this.handleError); ? it's confused – stackdave Jun 26 '17 at 20:42
  • the problem with this approach you can't get the erros when there is no response, like when server is offline, net::ERR_CONNECTION_REFUSED – stackdave Jun 26 '17 at 20:53
  • Becouse thouse example are waiting a Observable for later apply to them any subscriptor – alehn96 Jun 26 '17 at 20:55
  • all my services wait observables, and i'd like to manage error when server is off , or i get 401 , i ve followed this tutorial , but does not work for 401, https://scotch.io/courses/angular-2-http-and-observables/error-handling – stackdave Jun 26 '17 at 20:57
  • @alehn96 Your solution does not work for me, it does not change anything. First of all, since the request faces an error, the `.map` part is completely skipped. As if it does not exist. Also the second `console.log` still prints zero! – Mehran Jun 26 '17 at 21:21
0

I found out what was causing the issue.. Its a server side issue. You need to set the CORS middleware first then the remaining API middlewares.

Please note i am working with Laravel 5.6 + Angular 5

Wrong Code

'api' => [ 'throttle:60,1', 'bindings', \Barryvdh\Cors\HandleCors::class, ],

Currect Code

'api' => [ \Barryvdh\Cors\HandleCors::class, 'throttle:60,1', 'bindings' ],

Rameez Rami
  • 5,322
  • 2
  • 29
  • 36