typescript Angular2:如果 API 返回错误,如何重定向?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36627768/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:24:48  来源:igfitidea点击:

Angular2: how to redirect if API returns an error?

typescriptangularangular2-routingangular2-services

提问by Viktor

In my service, I want to describe a behavior when user is being redirected if not authorized.

在我的服务中,我想描述用户在未经授权的情况下被重定向时的行为。

export class MessagesService {
    constructor (private http: Http) {}

    private _usersUrl = '/users.json';  // URL to web api


    getUsers() {
        return this.http.get(this._usersUrl)
            .map(res => <User[]> res.json().data)
            .catch(this.handleError);
    }

    private handleError (error: Response) {

        if (error.status == 401) {
            // How do I tell Angular to navigate LoginComponent from here?
        } else {
            return Observable.throw(error.json().error || 'Server error');
        }
    }
}

My questions are:

我的问题是:

  • Is it even possible?
  • Is that a good practice?
    • If yes, how do I perform that?
    • If no, how else can I do that?
  • 甚至有可能吗?
  • 这是一个好习惯吗?
    • 如果是,我该如何执行?
    • 如果没有,我还能怎么做?

回答by rtn

My approach was to create my own request service and have an interceptor function which wraps the actual request to handle 401 and 403 etc.

我的方法是创建我自己的请求服务,并有一个拦截器函数来包装处理 401 和 403 等的实际请求。

Have included it below if you want to have a look at it.

如果您想查看它,请在下面包含它。

import {Injectable} from "@angular/core"
import {Subscription, Observable} from "rxjs"
import {TokenModel} from "../../models/token.model"
import {TokenService} from "../authentication/token.service"
import {Http, Headers, URLSearchParams, RequestOptions, Request, RequestMethod} from "@angular/http"
import {Router} from "@angular/router"

@Injectable()
export class RequestService
{
    private baseUrl: string;
    private subscription: Subscription;
    private token: TokenModel;

    constructor(public tokenService: TokenService,
                public http: Http,
                public router: Router)
    {
        this.baseUrl = `${process.env.API_URL}/example`;
        this.subscription = this.tokenService.token$.subscribe(token => this.token = token);
    }

    get(path: string, params?: Object, withCredentials?: boolean): Observable<any>
    {
        this.checkAuthorised();

        const url: string = this.baseUrl + path;
        const headers: Headers = new Headers({
            'Accept': 'application/json'
        });

        const searchParams = new URLSearchParams(`user_session=${this.token.token}`);

        for (let param in params) searchParams.set(param, params[param]);

        const options: RequestOptions = new RequestOptions({
            url: url,
            method: RequestMethod.Get,
            headers: headers,
            search: searchParams,
            withCredentials: withCredentials
        });

        const request = new Request(options);

        return this.makeRequest(request);
    }

    post(path: string, body?: Object, params?: Object, useDataProperty?: boolean, withCredentials?: boolean): Observable<any>
    {
        this.checkAuthorised();

        const url: string = this.baseUrl + path;

        const headers: Headers = new Headers({
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        });

        const data = JSON.stringify(useDataProperty ? {data: body} : body);

        const searchParams = new URLSearchParams(`user_session=${this.token.token}`);

        for (let param in params) searchParams.set(param, params[param]);

        const options: RequestOptions = new RequestOptions({
            url: url,
            method: RequestMethod.Post,
            headers: headers,
            body: data,
            search: searchParams,
            withCredentials: withCredentials
        });

        const request = new Request(options);

        return this.makeRequest(request);
    }

    makeRequest(request: Request)
    {
        return this.intercept(this.http.request(request).map(res => res.json()));
    }

    intercept(observable: Observable<any>)
    {
        return observable.catch(err =>
        {

            if (err.status === 401)
            {
                return this.unauthorised();

            } else if (err.status === 403)
            {
                return this.forbidden();
            } else
            {
                return Observable.throw(err);
            }
        });
    }

    unauthorised(): Observable<any>
    {
        this.tokenService.clear();
        this.router.navigate(['/login']);
        return Observable.empty();
    }

    forbidden(): Observable<any>
    {
        this.router.navigate(['/']);
        return Observable.empty();
    }

    checkAuthorised(): void
    {
        if (!this.token.token.length)
        {
            this.router.navigate(['login']);
        }
    }


}

回答by Willem Meints

One direction we've been approaching this on our team is by implementing an API client class. This API client wraps the original Http service.

我们团队一直在处理的一个方向是实现 API 客户端类。这个 API 客户端封装了原始的 Http 服务。

The idea is that since the Http service produces observables you can easily extend its behavior by adding operators like map, flatMapand catchoperators to the original observable produced by the Http service.

这个想法是,由于 Http 服务产生 observable,您可以通过向 Http 服务产生的原始 observable添加像mapflatMapcatch运算符这样的运算符来轻松扩展其行为。

I think you will find this example a useful starting point to fix the problem you're having.

我想你会发现这个例子是解决你遇到的问题的一个有用的起点。

import { ApiRequestOptions } from './api-request-options.service';
import { Http, Response, RequestOptions, ResponseContentType } from '@angular/http';

import 'rxjs/add/observable/zip';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Rx';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

@Injectable()
export class ApiClient {
    // PLease note, the API request options service is a helper that we introduced
    // to generate absolute URLs based on settings in the client.
    // I did not include it here for brevity.
    constructor(private router: Router, private http: Http, private requestOptions: ApiRequestOptions) {

    }

    get<TResponse>(path: string, queryStringParams?: any): Observable<TResponse> {
        let self = this;

        return Observable.zip(
            this.requestOptions.absoluteUrlFor(path, queryStringParams),
            this.requestOptions.authorizedRequestOptions()
        ).flatMap(requestOpts => {
            let [url, options] = requestOpts;
            return self.http.get(url, options);
        }).catch(response => {
            if (response.status === 401) {
                self.router.navigate(['/login']);
            }

            return response;
        }).map((response: Response) => <TResponse>response.json());
    }

    post<TResponse>(path: string, body: any): Observable<TResponse> {
        let self = this;

        return Observable.zip(
            this.requestOptions.absoluteUrlFor(path),
            this.requestOptions.authorizedRequestOptions()
        ).flatMap(requestOpts => {
            let [url, options] = requestOpts;
            return self.http.post(url, body, options);
        }).catch(response => {
            if (response.status === 401) {
                self.router.navigate(['/login']);
            }

            return response;
        }).map((response: Response) => <TResponse>response.json());
    }

    put<TResponse>(path: string, body: any): Observable<TResponse> {
        let self = this;

        return Observable.zip(
            this.requestOptions.absoluteUrlFor(path),
            this.requestOptions.authorizedRequestOptions()
        ).flatMap(requestOpts => {
            let [url, options] = requestOpts;
            return self.http.put(url, body, options);
        }).catch(response => {
            if (response.status === 401) {
                self.router.navigate(['/login']);
            }

            return response;
        }).map((response: Response) => {
            if (response.status === 200) {
                return <TResponse>response.json();
            } else {
                return null;
            }
        });
    }

    delete(path: string): Observable<Response> {
        let self = this;

        return Observable.zip(
            this.requestOptions.absoluteUrlFor(path),
            this.requestOptions.authorizedRequestOptions()
        ).flatMap(requestOpts => {
            let [url, options] = requestOpts;
            return self.http.delete(url, options);
        }).catch(response => {
            if (response.status === 401) {
                self.router.navigate(['/login']);
            }

            return response;
        });
    }
}