简体   繁体   中英

How to throw observable error in .map for a http status 200 response in Angular 4

In my Angular 4 app, I call a server's login api with http post. The server always responds with a stauts 200 whether or not the login was successful. I want to throw an error based on the server response data.

export class AuthenticationService {
...
login(username: string, password: string): Observable<any> {
    let body = `method=login&username=${username}&password=${password}`;

    return this.http.post('http://localhost/rest.php', body)
        .map((response: Response) => {
            let responsedata = response.json();
            if (responsedata && responsedata.success == 1) {
                localStorage.setItem('loggedin',1);
            } else {
                // Not logged in successfully
                Observable.throw(new Error('Not logged in'));
            }

        });
}

And my code calling login()

trytologin() {
    this.loading = true;
    this.authenticationService.login(this.model.username, this.model.password)
        .subscribe(
            data => {
                // Always returns here regarless of result of login
                this.router.navigate([this.returnUrl]);
            },
            error => {
                // ONLY returns here on a NON-server 200 status
                this.alertService.error(error);
                this.loading = false;
            });
}

I think I could intercept the response and change the headers by using HttpInterceptor , however this seems like overkill. Is there another way to get the subscribe error to trigger from a 200 status response?


EDIT

I tried implementing a HttpInterceptor without success.

import {HttpClient, HttpHeaders} from '@angular/common/http';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse,HttpHeaderResponse, HttpErrorResponse} from '@angular/common/http';
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/map';

@Injectable()
export class LoginInterceptor implements HttpInterceptor {

constructor() {}

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // Need to .map so we can go through each event looking for the HttpResponse
    // Can't use .do() as it doesn't update per the docs
    return next.handle(req).map(event => {
        if (event instanceof HttpResponse) {

            let e2 = new HttpErrorResponse({status: 405, statusText: 'Auth error'});
            // return Observable(e2);   // Cannot return as HttpErrorResponse is NOT part of HttpEvent<any>
            Observable.throw(e2);  // <-- Still triggers the success in .subscribe().  Does NOT trigger the .subscribe() error
            event = event.clone({status: 405, statusText: 'Auth error'});  // <-- change the event status.  Can't return HttpErrorResponse as
        }

        return event;
    });

}
}

The .subscribe() success is always called.


EDIT #2 编辑#2

After LOTS of digging, I now know why my code was failing. But I still don't have a solution!!

The HttpInterceptor is the way to go..... but the Observer.throw() will only work only if the HttpClient.post() is on the same domain!! The subscribe() error is called if I post() to localhost. If I call a post() to another domain, it fails to call the subscribe() error. I put a console.log() immediately after the Observer.throw() . There is log output if I have a different domain. There is no log output if the domain is the same. I don't know if this is a CORS issue as I have control over the server output and have included a Access-Control-Allow-Origin: * header item.

Is this possibly a bug in the HttpClient and/or HttpInterceptor ??? Or maybe a problem with rxjs??


Edit #3

If I simply return an Observable.throw() in the intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> block my error code gets triggered. This however will trigger on the http request only. I need to process and throw an error on the . 上引发错误。 In order to do this I need to use return next.handle(req).map() to look for each HttpResponse event.

If I try and execute an Observer.throw() inside the map() it does NOT trigger my error code and is not caught anywhere. The line after the Observer.throw() is executed.

I can't return Observer.throw() from the map() as it is the incorrect return type.

However, calling a plain javascript throw() inside of the map() will trigger my error code.

I'm not sure if a plain throw() is best practice or if I should be doing something else.

You will have to import proper libraries and methods of Observable to get this code working like

import { HttpInterceptor, HttpErrorResponse } from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/Observable/of';

You can intercept the response and from that response you can take a decision,

export class YourInterceptor implements HttpInterceptor{
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>>{
              //check your conditions if fall true send this response as given //below
                return Observable.throw(new HttpErrorResponse({status:401, statusText:"Error of auth"}));
        }
}

Find the screenshot of a sample code that I tried today, 调用服务的组件代码

浏览器console.log输出

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM