Как правильно использовать responseError с пользовательскими перехватчиками Aurelia http-fetch-client?

Я работаю над проектом Aurelia CLI, используя TypeScript, и aurelia-fetch-client для выполнения HTTP-вызовов на серверную часть.NET Core / Web API.

Сервер стандартизирует необработанные исключения, возвращая результат JSON ApiError в виде тела ответов API с кодом состояния в диапазоне 5xx.

На клиенте я хочу использовать перехватчик, который выполняет публикацию этих ошибок на стороне клиента, но добавление перехватчика изменяет поток управления нежелательным образом.

Это соответствующий конфиг в main.ts:

function configureContainer(container: Container) {
    const http = new HttpClient();
    http.configure((config: HttpClientConfiguration) => {
        config.useStandardConfiguration()
            .withBaseUrl("/api/")
            .withInterceptor(new ApiErrorInterceptor(container.get(EventAggregator)));
    });

    container.registerInstance(HttpClient, http);
}

И это перехватчик:

import { autoinject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";
import { Interceptor } from "aurelia-fetch-client";

@autoinject
export class ApiErrorInterceptor implements Interceptor {
    constructor(private readonly _aggregator: EventAggregator) {
    }

    responseError(error: any) {
        // do something with the error            

        return error;
    }
}

Если пользовательский перехватчик НЕ добавлен, то не-OK ответ регистрируется как ошибка в Chrome, например:

Unhandled rejection (<{}>, no stack trace)

Но если перехватчик добавлен, то обещания больше не приводят к необработанной ошибке отклонения, и вызывающий код продолжается, как будто обещание было разрешено, а это НЕ то, что мы хотим, потому что это вносит существенные изменения в поток управления все приложение.

Как мне реализовать responseError() обработчик, чтобы поток управления работал так же, как и раньше? Это вообще возможно, или я неправильно понимаю цель этого обработчика?

Я попытался повторно выдать ошибку вместо возврата, но это не скомпилируется с интерфейсом Interceptor. Должен ли я делать это в response() вместо, например?

РЕДАКТИРОВАТЬ:

Это то, чем мы в конечном итоге воспользовались, после более детального изучения источника Fetch и связанных с ним источников Aurelia. Он обрабатывает серверные ошибки исключительно в обработчике response(), а клиентские ошибки подключения - только в responseError().

import { autoinject } from "aurelia-framework";
import { EventAggregator } from "aurelia-event-aggregator";
import { Interceptor } from "aurelia-fetch-client";
import { TypedJSON } from "typedjson-npm";
import { EventNames } from "../model/EventNames";
import { ApiError } from "../model/ApiError";

@autoinject
export class ApiErrorInterceptor implements Interceptor {
    constructor(private readonly _aggregator: EventAggregator) {
    }

    async response(response: Response, request?: Request) {
        if (!response.ok) {
            // publish an error for the response
            const apiError = await this.translateToApiError(response.clone());
            if (apiError) {
                this._aggregator.publish(EventNames.apiError, apiError);
            }
        }

        return response;
    }

    responseError(error: any) {
        // publish errors resulting from connection failure, etc
        if (error instanceof Error) {
            const apiError = new ApiError();
            apiError.statusCode = 0;
            apiError.isError = true;
            apiError.message = error.message;

            this._aggregator.publish(EventNames.apiError, apiError);
        }

        return error;
    }

    private async translateToApiError(response: Response): Promise<ApiError> {
        let apiError: ApiError | undefined = undefined;
        const text = await response.text();

        if (text) {
            apiError = TypedJSON.parse(text, ApiError);
        }

        if (!apiError) {
            apiError = this.getHttpError(response);
        }

        return apiError;
    }

    private getHttpError(response: Response): ApiError {
        const apiError = new ApiError();
        apiError.isError = true;
        apiError.statusCode = response.status;
        apiError.message = "Unknown HTTP Error";

        switch (apiError.statusCode) {
            case 400:
                apiError.message = "Bad Request";
                break;

            case 401:
                apiError.message = "Unauthorized Access";
                break;

            case 404:
                apiError.message = "Not Found";
                break;
        }

        if (apiError.statusCode >= 500 && apiError.statusCode < 600) {
            apiError.message = "Internal Server Error";
        }

        return apiError;
    }
}

2 ответа

С помощью return Promise.reject(error); на месте return error; компилируется и работает, как ожидается, используя ваш код. Возврат ошибки предполагает, что вы разрешили ответ и передали возвращаемое значение в путь разрешенного кода, поэтому вы видели нежелательное поведение.

Параметр ошибки responseError любой, поэтому убедитесь, что это то, что вы думаете. В моем случае я ожидал неудавшегося ответа, но получил пойманную TypeError исключение. Это мой перехватчик:

responseError(response: any): Promise<Response> {
    if (response instanceof Response) {
        return response.json().then((serverError: ServerError) => {

            // Do something with the error here.

            return Promise.reject<Response>(serverError.error);
        }); 
    }
}

У меня также были проблемы, связанные с тем, как мое промежуточное ПО для обработки исключений настроено в приложении.NET Core. В моем случае UseDeveloperExceptionPage вызывал ошибку CORS в клиенте выборки. Не похоже, что это та же проблема, что и у вас, но вы можете сравнить свою конфигурацию с моей здесь.

Другие вопросы по тегам