ts2339 - Errores de captura en Angular HttpClient
property get does not exist on type httpclient (10)
Tengo un servicio de datos que se ve así:
@Injectable()
export class DataService {
baseUrl = ''http://localhost''
constructor(
private httpClient: HttpClient) {
}
get(url, params): Promise<Object> {
return this.sendRequest(this.baseUrl + url, ''get'', null, params)
.map((res) => {
return res as Object
})
.toPromise();
}
post(url, body): Promise<Object> {
return this.sendRequest(this.baseUrl + url, ''post'', body)
.map((res) => {
return res as Object
})
.toPromise();
}
patch(url, body): Promise<Object> {
return this.sendRequest(this.baseUrl + url, ''patch'', body)
.map((res) => {
return res as Object
})
.toPromise();
}
sendRequest(url, type, body, params = null): Observable<any> {
return this.httpClient[type](url, { params: params }, body)
}
}
Si recibo un error HTTP (es decir, 404), recibo un mensaje desagradable de la consola: ERROR Error: No capturado (en promesa): [objeto Objeto] de core.es5.js ¿Cómo lo manejo en mi caso?
Al usar Interceptor puede detectar errores. Debajo está el código:
@Injectable()
export class ResponseInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//Get Auth Token from Service which we want to pass thr service call
const authToken: any = `Bearer ${sessionStorage.getItem(''jwtToken'')}`
// Clone the service request and alter original headers with auth token.
const authReq = req.clone({
headers: req.headers.set(''Content-Type'', ''application/json'').set(''Authorization'', authToken)
});
const authReq = req.clone({ setHeaders: { ''Authorization'': authToken, ''Content-Type'': ''application/json''} });
// Send cloned request with header to the next handler.
return next.handle(authReq).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
console.log("Service Response thr Interceptor");
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
console.log("err.status", err);
if (err.status === 401 || err.status === 403) {
location.href = ''/login'';
console.log("Unauthorized Request - In case of Auth Token Expired");
}
}
});
}
}
Puede preferir este blog ... dado un ejemplo simple para ello.
Bastante sencillo (en comparación con cómo se hizo con la API anterior).
Fuente de (copiado y pegado) la guía oficial angular
get(url, params): Promise<Object> {
return this.sendRequest(this.baseUrl + url, ''get'', null, params)
.map((res) => {
return res as Object
}).catch((e) => {
return Observable.of(e);
})
.toPromise();
}
Con la llegada de la API
HTTPClient
, no solo se reemplazó la API
Http
, sino que se agregó una nueva, la API
HttpInterceptor
.
AFAIK uno de sus objetivos es agregar un comportamiento predeterminado a todas las solicitudes salientes HTTP y respuestas entrantes.
Suponiendo que desea agregar un
comportamiento predeterminado de manejo de errores
, agregar
.catch()
a todos sus posibles métodos http.get / post / etc es ridículamente difícil de mantener.
Esto podría hacerse de la siguiente manera como ejemplo usando un
HttpInterceptor
:
import { Injectable } from ''@angular/core'';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse, HTTP_INTERCEPTORS } from ''@angular/common/http'';
import { Observable } from ''rxjs/Observable'';
import { _throw } from ''rxjs/observable/throw'';
import ''rxjs/add/operator/catch'';
/**
* Intercepts the HTTP responses, and in case that an error/exception is thrown, handles it
* and extract the relevant information of it.
*/
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
/**
* Intercepts an outgoing HTTP request, executes it and handles any error that could be triggered in execution.
* @see HttpInterceptor
* @param req the outgoing HTTP request
* @param next a HTTP request handler
*/
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.catch(errorResponse => {
let errMsg: string;
if (errorResponse instanceof HttpErrorResponse) {
const err = errorResponse.message || JSON.stringify(errorResponse.error);
errMsg = `${errorResponse.status} - ${errorResponse.statusText || ''''} Details: ${err}`;
} else {
errMsg = errorResponse.message ? errorResponse.message : errorResponse.toString();
}
return _throw(errMsg);
});
}
}
/**
* Provider POJO for the interceptor
*/
export const ErrorInterceptorProvider = {
provide: HTTP_INTERCEPTORS,
useClass: ErrorInterceptor,
multi: true,
};
// app.module.ts
import { ErrorInterceptorProvider } from ''somewhere/in/your/src/folder'';
@NgModule({
...
providers: [
...
ErrorInterceptorProvider,
....
],
...
})
export class AppModule {}
Alguna información adicional para OP: llamar a http.get / post / etc sin un tipo fuerte no es un uso óptimo de la API. Su servicio debería verse así:
// These interfaces could be somewhere else in your src folder, not necessarily in your service file
export interface FooPost {
// Define the form of the object in JSON format that your
// expect from the backend on post
}
export interface FooPatch {
// Define the form of the object in JSON format that your
// expect from the backend on patch
}
export interface FooGet {
// Define the form of the object in JSON format that your
// expect from the backend on get
}
@Injectable()
export class DataService {
baseUrl = ''http://localhost''
constructor(
private http: HttpClient) {
}
get(url, params): Observable<FooGet> {
return this.http.get<FooGet>(this.baseUrl + url, params);
}
post(url, body): Observable<FooPost> {
return this.http.post<FooPost>(this.baseUrl + url, body);
}
patch(url, body): Observable<FooPatch> {
return this.http.patch<FooPatch>(this.baseUrl + url, body);
}
}
Devolver
Promises
de sus métodos de servicio en lugar de
Observables
es otra mala decisión.
Y un consejo adicional: si está usando la secuencia de comandos TYPE , comience a usar la parte tipo. Pierde una de las mayores ventajas del lenguaje: saber el tipo de valor con el que está tratando.
Si desea un, en mi opinión, un buen ejemplo de un servicio angular, eche un vistazo a la siguiente idea .
Para Angular 6+, .catch no funciona directamente con Observable. Tienes que usar
import { IEmployee } from ''./interfaces/employee'';
import { Injectable } from ''@angular/core'';
import { HttpClient, HttpErrorResponse } from ''@angular/common/http'';
import { Observable, throwError } from ''rxjs'';
import { catchError } from ''rxjs/operators'';
@Injectable({
providedIn: ''root''
})
export class EmployeeService {
private url = ''/assets/data/employee.json'';
constructor(private http: HttpClient) { }
getEmployees(): Observable<IEmployee[]> {
return this.http.get<IEmployee[]>(this.url)
.pipe(catchError(this.errorHandler)); // catch error
}
/** Error Handling method */
errorHandler(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error(''An error occurred:'', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError(
''Something bad happened; please try again later.'');
}
}
Debajo del código:
this.sendRequest(...)
.map(...)
.catch((err) => {
//handle your error here
})
Para más detalles, consulte la Guía angular para Http
Permítanme actualizar la respuesta de HttpInterceptor sobre el uso de HttpInterceptor con las últimas funciones de RxJs (v.6).
import { Injectable } from ''@angular/core'';
import {
HttpInterceptor,
HttpRequest,
HttpErrorResponse,
HttpHandler,
HttpEvent,
HttpResponse
} from ''@angular/common/http'';
import { Observable, EMPTY, throwError, of } from ''rxjs'';
import { catchError } from ''rxjs/operators'';
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request).pipe(
catchError((error: HttpErrorResponse) => {
if (error.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error(''An error occurred:'', error.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${error.status}, body was: ${error.error}`);
}
// If you want to return a new response:
//return of(new HttpResponse({body: [{name: "Default value..."}]}));
// If you want to return the error on the upper level:
//return throwError(error);
// or just return nothing:
return EMPTY;
})
);
}
}
Probablemente quieras tener algo como esto:
http
.get<ItemsResponse>(''/api/items'')
.subscribe(
// Successful responses call the first callback.
data => {...},
// Errors will call this callback instead:
err => {
console.log(''Something went wrong!'');
}
);
Depende mucho también de cómo utilice su servicio, pero este es el caso básico.
Si no puede detectar errores con ninguna de las soluciones proporcionadas aquí, es posible que el servidor no esté manejando las solicitudes CORS.
En ese caso, Javascript, mucho menos Angular, puede acceder a la información de error.
Busque advertencias en su consola que incluyan
CORB
o
Cross-Origin Read Blocking
.
Además, la sintaxis ha cambiado para manejar errores (como se describe en cualquier otra respuesta). Ahora usa operadores aptos para la canalización, así:
import { Observable, throwError } from ''rxjs'';
import { catchError } from ''rxjs/operators'';
const PASSENGER_API = ''api/passengers'';
getPassengers(): Observable<Passenger[]> {
return this.http
.get<Passenger[]>(PASSENGER_API)
.pipe(catchError((error: HttpErrorResponse) => throwError(error)));
}
Siguiendo la respuesta de @acdcjunior, así es como lo implementé
Servicio:
this.dataService.get(baseUrl, params)
.then((object) => {
if(object[''name''] === ''HttpErrorResponse'') {
this.error = true;
//or any handle
} else {
this.myObj = object as MyClass
}
});
llamador:
this.service.requestsMyInfo(payload).pipe(
catcheError(err => {
// handle the error here.
})
);
Tienes algunas opciones, dependiendo de tus necesidades.
Si desea manejar los errores por solicitud, agregue una
catch
a su solicitud.
Si desea agregar una solución global, use
HttpInterceptor
.
Abra aquí el plunker de demostración de trabajo para las soluciones a continuación.
tl; dr
En el caso más simple, solo necesitará agregar un
.catch()
o un
.subscribe()
, como:
import ''rxjs/add/operator/catch''; // don''t forget this, or you''ll get a runtime error
this.httpClient
.get("data-url")
.catch((err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below
console.error(''An error occurred:'', err.error);
});
// or
this.httpClient
.get("data-url")
.subscribe(
data => console.log(''success'', data),
error => console.log(''oops'', error)
);
Pero hay más detalles sobre esto, ver más abajo.
Solución de método (local): error de registro y respuesta de retorno de retorno
Si necesita manejar errores en un solo lugar, puede usar
catch
y devolver un valor predeterminado (o una respuesta vacía) en lugar de fallar por completo.
Tampoco necesita el
.map
solo para emitir, puede usar una función genérica.
Fuente:
Angular.io - Obteniendo detalles del error
.
Entonces, un
.get()
genérico
.get()
sería como:
import { Injectable } from ''@angular/core'';
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { Observable } from ''rxjs/Observable'';
import ''rxjs/add/operator/catch'';
import ''rxjs/add/observable/of'';
import ''rxjs/add/observable/empty'';
import ''rxjs/add/operator/retry''; // don''t forget the imports
@Injectable()
export class DataService {
baseUrl = ''http://localhost'';
constructor(private httpClient: HttpClient) { }
// notice the <T>, making the method generic
get<T>(url, params): Observable<T> {
return this.httpClient
.get<T>(this.baseUrl + url, {params})
.retry(3) // optionally add the retry
.catch((err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error(''An error occurred:'', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
}
// ...optionally return a default fallback value so app can continue (pick one)
// which could be a default value
// return Observable.of<any>({my: "default value..."});
// or simply an empty observable
return Observable.empty<T>();
});
}
}
Manejar el error permitirá que su aplicación continúe incluso cuando el servicio en la URL esté en malas condiciones.
Esta solución por solicitud es buena principalmente cuando desea devolver una respuesta predeterminada específica a cada método. Pero si solo le preocupa la visualización de errores (o tiene una respuesta global predeterminada), la mejor solución es usar un interceptor, como se describe a continuación.
Ejecute el demostrador de trabajo aquí .
Uso avanzado: interceptar todas las solicitudes o respuestas
Una vez más, la guía Angular.io muestra:
Una característica importante de
@angular/common/http
es la intercepción, la capacidad de declarar interceptores que se encuentran entre su aplicación y el back-end. Cuando su aplicación realiza una solicitud, los interceptores la transforman antes de enviarla al servidor, y los interceptores pueden transformar la respuesta a su regreso antes de que su aplicación la vea. Esto es útil para todo, desde la autenticación hasta el registro.
Que, por supuesto, se puede usar para manejar errores de una manera muy simple ( demo plunker aquí ):
import { Injectable } from ''@angular/core'';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse,
HttpErrorResponse } from ''@angular/common/http'';
import { Observable } from ''rxjs/Observable'';
import ''rxjs/add/operator/catch'';
import ''rxjs/add/observable/of'';
import ''rxjs/add/observable/empty'';
import ''rxjs/add/operator/retry''; // don''t forget the imports
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.catch((err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.error(''An error occurred:'', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.error(`Backend returned code ${err.status}, body was: ${err.error}`);
}
// ...optionally return a default fallback value so app can continue (pick one)
// which could be a default value (which has to be a HttpResponse here)
// return Observable.of(new HttpResponse({body: [{name: "Default value..."}]}));
// or simply an empty observable
return Observable.empty<HttpEvent<any>>();
});
}
}
Proporcionar su interceptor:
simplemente declarar el
HttpErrorInterceptor
anterior no hace que su aplicación lo use.
Debe
conectarlo en el módulo de su aplicación
proporcionándolo como un interceptor, de la siguiente manera:
import { NgModule } from ''@angular/core'';
import { HTTP_INTERCEPTORS } from ''@angular/common/http'';
import { HttpErrorInterceptor } from ''./path/http-error.interceptor'';
@NgModule({
...
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: HttpErrorInterceptor,
multi: true,
}],
...
})
export class AppModule {}
Nota: Si tiene un interceptor de errores y algo de manejo local de errores, naturalmente, es probable que nunca se active un manejo local de errores, ya que el error siempre será manejado por el interceptor antes de que llegue al manejo local de errores.
Ejecute el demostrador de trabajo aquí .
import { Observable, throwError } from ''rxjs''; import { catchError } from ''rxjs/operators''; const PASSENGER_API = ''api/passengers''; getPassengers(): Observable<Passenger[]> { return this.http .get<Passenger[]>(PASSENGER_API) .pipe(catchError((error: HttpErrorResponse) => throwError(error))); }