J'ai créé un REST appel API dans mon Angular app qui télécharge un fichier.
Je mets responseType sur 'blob' car j'attends un fichier en réponse.
Mais quand aucun fichier n'est disponible sur le serveur, la réponse a un code d'erreur 404, c'est-à-dire une mauvaise demande avec un message dans le corps.
Mais je ne suis pas en mesure d'analyser ce message d'erreur du corps car HttpErrorResponse donne un objet blob dans error.error
Comment puis-je obtenir le corps réel de l'objet d'erreur au lieu de blob.
Existe-t-il également un moyen de configurer angular qui, en cas de succès d'un appel api, analyse la demande en blob, sinon l'analyser en json ???
En espérant une résolution
Si le ContentType retourné est différent, vous pouvez l'utiliser pour distinguer s'il s'agit d'un fichier binaire correct ou d'un texte au format binaire.
considérons que vous avez deux fichiers, un service, qui gère votre demande et un composant qui fait la logique métier
Dans votre service, ayez votre méthode de téléchargement comme:
public downloadFile(yourParams): Observable<yourType | Blob> {
return this._http.post(yourRequestURL, yourParams.body, {responseType: 'blob'}).pipe(
switchMap((data: Blob) => {
if (data.type == <ResponseType> 'application/octet-stream') {
// this is a correct binary data, great return as it is
return of(data);
} else {
// this is some error message, returned as a blob
let reader = new FileReader();
reader.readAsBinaryString(data); // read that message
return fromEvent(reader, 'loadend').pipe(
map(() => {
return JSON.parse(reader.result); // parse it as it's a text.
// considering you are handling JSON data in your app, if not then return as it is
})
);
}
})
);
}
Dans votre composant
public downloadFile(params): void {
this._service.downloadFile(params)
subscribe((data: yourType | Blob) => {
if (data instanceof Blob) {
fileSaverSave(data, filename); // use fileSaver or however you are downloading the content
// add an import for saveAs (import { saveAs as fileSaverSave } from 'file-saver';)
} else {
// do your componnet logic to show the errors
}
})
}
Si vous le souhaitez, vous pouvez tout avoir à l'intérieur de votre composant lui-même.
Paramètre: {observez: 'réponse'} , vous permet de lire la réponse complète, y compris les en-têtes. Voir la description ci-dessous: -
Dites à HttpClient que vous voulez la réponse complète avec l'option observe:
getConfigResponse(): Observable<HttpResponse<Config>> {
return this.http.get<Config>(this.configUrl, { observe: 'response' });
}
Maintenant, HttpClient.get () renvoie un Observable de HttpResponse typé plutôt que juste les données JSON.
this.configService.getConfigResponse()
// resp is of type `HttpResponse<Config>`
.subscribe(resp => {
// display its headers
const keys = resp.headers.keys();
this.headers = keys.map(key =>
`${key}: ${resp.headers.get(key)}`);
// access the body directly, which is typed as `Config`.
this.config = { ...resp.body };
});
et obtenir le corps d'erreur comme ça: -
private handleError(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.');
};
importer {catchError} depuis 'rxjs/operators';
getConfig() { return this.http.get<Config>(this.configUrl) .pipe( catchError(this.handleError) ); }
Référence: https://angular.io/guide/http : lecture de la réponse complète
Modifiez votre code en conséquence.
Vous pouvez essayer une fonction de gestionnaire d'erreurs distincte, qui renvoie la réponse sous la forme
T
comme suit -
public handleError<T>(operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an empty result.
return of(result as T);
};
}
Ensuite, utilisez-le simplement pour suivre les erreurs dans votre demande comme suit -
return this.http.post(this.appconstants.downloadUrl, data, { responseType: 'blob' }).pipe(
map(this.loggerService.extractFiles),
catchError(this.loggerService.handleError<any>('downloadFile')) // <----
);
Pour info, la fonction extractFiles
que j'ai utilisée ci-dessus pour renvoyer un fichier est la suivante -
public extractFiles(res: Blob): Blob{
return res;
}
Pour les futurs visiteurs (puisque le titre est générique):
Si le backend renvoie JSON en cas d'erreur (idéalement, en suivant RFC 7807 , ce qui signifierait également le type de contenu application/problem+json
), Le error.error
est un objet JSON, pas une chaîne . Ainsi, pour l'imprimer, par exemple, vous devez d'abord le filtrer:
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${JSON.stringify(error.error)}`);
Je pense que la confusion vient de l'officiel Angular documentation , qui contient cette déclaration:
// 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}`);
Mais avec error.error
Étant un objet JSON (dans les cas standard), vous obtenez [object Object]
Imprimé pour le corps au lieu de la représentation sous forme de chaîne de cet objet JSON. Même sortie inutile si vous essayez ${error.error.toString()}
.