Как получить тело / содержание запроса от HttpErrorResponse? [Angular ErrorHandler]
Допустим, я отправил post
запрос на сервер для создания пользователя. Если сервер отвечает с ошибкой (по какой-то причине), я хочу получить тело (форму), которую я отправил (приложил с этим запросом) от ErrorHandler
, Причина этого заключается в том, что, например, когда "создать пользователя" не удается, я хочу показать уведомление с некоторыми подробностями, взятыми из формы, и кнопку, чтобы перенаправить вас обратно на соответствующую страницу с полями, заполненными снова извлеченными форма.
Вот так мой ErrorHandler
похоже:
@Injectable()
export class ErrorsHandler implements ErrorHandler {
constructor(
private injector: Injector,
) { }
handleError(error: Error | HttpErrorResponse) {
const errorsService = this.injector.get(ErrorsService);
const router = this.injector.get(Router);
const zone = this.injector.get(NgZone);
if (error instanceof HttpErrorResponse) {
// Server error happened
if (!navigator.onLine) {
return console.log('No Internet Connection.');
}
else if (error.status === HttpStatus.UNAUTHORIZED) {
console.log('ErrorsHandler handled HttpStatus Unauthorized. Navigating back to \'/login\' page.');
zone.run(() => router.navigate(['/login']));
}
else {
// Http Error
//How do I get the form from here? I need it for user notification.
return console.log('%c SERVER ERROR ', 'background: #222; color: #ff6961 ; font-size: 15px; border: 2px solid #ff6961;', error);
}
} else {
// Client Error Happend
// Send the error to the server and then
// redirect the user to the page with all the info
errorsService
.log(error)
.subscribe(errorWithContextInfo => {
router.navigate(['/error'], { queryParams: errorWithContextInfo });
});
}
}
}
2 ответа
У меня есть вариант ответа @GabrielLopez с перехватчиком:
import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse}
from "@angular/common/http";
import {Observable, throwError} from "rxjs";
import {catchError, tap} from "rxjs/operators";
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>,
next: HttpHandler):
Observable<HttpEvent<any>> {
return next.handle(req)
.pipe(
tap((ev: HttpEvent<any>) => {
if (ev instanceof HttpResponse) {
console.log(`processing response ${ev.status}`);
}
}),
catchError(response => {
console.log('Processing http error', response);
if (response.error) {
return throwError(response.error);
} else if (response.message) {
return throwError(response.message);
} else {
return throwError(response);
}
})
);
}
}
И как и ответ Габриэля, перехватчик должен быть объявлен в app.module.ts providers
:
@NgModule({
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true
},
...
Я не доволен этим, потому что, возможно, это означает, что обработка ошибок в Angular HttpClient слишком мощная. Получение ошибки от вызова API REST не должно быть сложным.
Я думаю, что невозможно получить тело из экземпляра HttpErrorResponse, поскольку он расширяет HttpResponseBase, у которого нет свойства body, как у обычного HttpResponse.
export declare class HttpErrorResponse extends HttpResponseBase implements Error {
readonly name: string;
readonly message: string;
readonly error: any | null;
/**
* Errors are never okay, even when the status code is in the 2xx success range.
*/
readonly ok: boolean;
constructor(init: {
error?: any;
headers?: HttpHeaders;
status?: number;
statusText?: string;
url?: string;
});
}
Что я сделал, так это использовал Response Incerceptor:
import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ResponseBusiness } from '../Models/General/ResponseBusiness.model';
import { MsgService } from '../services/msg.service';
import { AlertService } from '../services/alert.service';
@Injectable()
export class ResponseInterceptor implements HttpInterceptor {
constructor(private _msg: MsgService, private _alertService: AlertService) {
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).map(resp => {
const response = <HttpResponse<ResponseBusiness<Object>>> resp;
if (resp instanceof HttpResponse) {
}
/* do whatever you need with the req.body */
if (resp instanceof HttpErrorResponse) {
const body = JSON.parse(req.body);
if (body && body.avoidMsg) {
return resp;
}
}
if (response.status === 200 && !response.body.result.status) {
this._msg.setMsg({message: `${response.body.result.codeNumber} ${response.body.result.codeDescription}`, tipo: 'error'});
}
return resp;
});
}
}
Затем добавьте inteceptor к вашему app.module следующим образом:
providers: [
{provide: HTTP_INTERCEPTORS, useClass: ResponseInterceptor, multi: true}],
Прежде всего, вы должны подтвердить, что BE возвращает ошибку JSON в теле. Следующим шагом вы можете настроить HttpInterceptor для вашей идеи, более подробную информацию вы можете найти по ключевым словам угловой httpinterceptor.
Это мой источник для HttpInterceptor, может быть некоторая помощь.
import { Injectable } from '@angular/core';
import { HttpRequest, HttpInterceptor, HttpHandler, HttpEvent, HttpResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { tap, catchError } from 'rxjs/operators';
@Injectable()
export class SiteHttpInterceptor implements HttpInterceptor {
constructor(
) {}
intercept(request: HttpRequest<any>, httpHandler: HttpHandler): Observable<any> {
let token = localStorage.getItem('token');
if (('/api/users/token').indexOf(request.url) < 0 && token) {
request = request.clone({
setHeaders: {
'authorization': 'bearer ' + token,
'Authenticationtoken': 'bearer ' + token
}
});
}
return httpHandler.handle(request).pipe(
tap((event: HttpEvent<any>) => {
//success
},
(err: any) => {
//error
}),
catchError(err => {
if (err.status === 401) {
// if you want to do sth. when 401 can code here
} else {
// other
}
return throwError(err);
})
);
}
}
и, пожалуйста, настройте HttpInterceptor для app.module.ts
import { SiteHttpInterceptor } from './providers/http-interceptor';
@NgModule({
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }]
Дайте мне знать, это нормально для вас или нет:)