Comunicación entre Angular y un Servidor utilizando una API REST y httpclient
En este proyecto vamos a realizar una serie de ejemplo de cómo trabajar con una API REST en Angular. Ya anteriormente hemos visto realizar este tipo de comunicaciones con la librería XMLHttpRequest pero realmente con este tutorial queremos evolucionar y mejorar nuestras aplicaciones utilizando HttpClient de Angular. Con esta mejora lo que pretendemos es que la captación de eventos y las peticiones a la API sea mucho más rápidas y eficientes haciendo comunicaciones asíncronas y captando los errores producidos. La principal consecuencia de esta evolución la puedes percibir en la experiencia de usuario. La aplicación continue en funcionamiento mientras espera la respuesta de la petición realizada al servidor, es decir, tenemos peticiones en segundo plano. El código utilizado en este artículo lo puedes obtener del siguiente repositorio https://github.com/al118345/TestComunicacionApiRest_Angular. El código utilizado en la API REST lo puedes obtener del siguiente repositorio: https://github.com/al118345/envio_email_api_python
constructor(private http: HttpClient) { }
/*
Cómunicación con una API con el objetivo de obtener un saludo de bienvenida.
return: Observable<any>
*/
saludo(): Observable<any> {
return this.http.get<any>( url_api_rest/saludo/inicial)
.pipe(
catchError((err) => {
console.log('error caught in service')
console.error(err);
return throwError(err);
})
);
}
Yo normalmente creo un servicio que posteriormente es utilizado desde el componente.ts, por ejemplo, en este caso he creado ConsultarapiService que lo he utilizado de la siguiente forma:
Prueba este ejemplo con la ejecución de este botón: Fíjate que existe un tiempo de espera en que la aplicación no está parada y aparece un cargando. Esto se debe a que httpclient se encarga de realizar la llama asíncrona y, además, de gestionar todos los errores que puedan ocurrir
Ejemplo petición POST - Login
Antes de empezar, si quieres descargar el ejemplo explicado en este punto visita el siguiente enlace: https://github.com/al118345/TestComunicacionApiRest_Angular/tree/master/src/app/components/login Otro ejemplo de petición POST es la de enviar un formulario con usuario y contraseña para realizar un login en nuestro servidor. Por ejemplo queremos realizar una autenticación OAuth2.0 (más información en Wikipedia https://es.wikipedia.org/wiki/OAuth) en nuestro servidor y, por lo tanto, enviamos el usuario y la contraseña con el siguiente comando:
/*
Cómunicación con una API de ejemplo para obtener un token de autenticación.
return : Observable<User>
*/
login(username: string, password: string): Observable<User> {
const data = {
'user': username,
'password': password
};
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
})
};
return this.http.post<User>( url_api_rest/login
, data, httpOptions)
.pipe(
catchError((err) => {
console.log('error caught in service')
console.error(err);
return throwError(err);
})
);
}
Si se observa el código, lo que hacemos es enviar una petición POST con la información del usuario y contraseña para autentificarnos en el sistema. El resultado obtenido es un JSON con un token de autenticación. Hemos definido un modelo usuario en el sistema con la siguiente configuración:
export class User {
constructor(
public user: string,
public passwd: string,
public token: string
) {
}
}
La respuesta del servidor es mapeada en el modelo usuario. En ese momento tenemos acceso al token que nos permite realizar las peticiones que queramos al servidor.
Ejemplo petición POST con filtrado de la respuesta recibida utilizando map.
Antes de empezar, si quieres descargar el ejemplo explicado en este punto visita el siguiente enlace: https://github.com/al118345/TestComunicacionApiRest_Angular/tree/master/src/app/components/obtener-json El título de esta sección es extraño de explicar pero con un ejemplo se entenderá perfectamente. Realizamos una petición POST a la API y el servidor nos puede devolver una estructura de respuesta cómo la siguiente:
Observa que la propia respuesta de la API tiene una key llamada datos. Esta key contiene la información que nos interesa y, por lo tanto, mapeamos la respuesta para que el método devuelva no el objeto completo sino la parte del Json que nos interesa. A continuación muestro la vista correctamente configurada:
<div class="row">
<div class="col-md-10">
Ejemplo de obtención de un documento JSON desde una API REST
</div>
</div>
<p></p>
<div class="row">
<div class="col-md-5">
<button type="submit" [disabled]="cargando" class="btn btn-primary"
(click)="obtener_json()">Obtener JSON</button>
<ng-container *ngIf="printado">
<div class="alert-info">
<p>{{this.resultado}}</p>
<pre>{{this.object_json | json}}</pre>
</div>
</ng-container>
<ng-container *ngIf="cargando">
<mat-spinner mode="indeterminate" ></mat-spinner>
</ng-container>
</div>
</div>
Una vez almacenado el fichero, podemos enviarlo a nuestro servidor a través de una petición a nuestra API cómo la siguiente implementación:
/*
Función para enviar un fichero a la api
params: formData: el formulario con el fichero
return: Observable<any>
*/
enviar_documento(formData:any): Observable<any> {
const headers = new HttpHeaders().set('Accept', 'application/pdf');
return this.http.post<any>(`http://0.0.0.0:5000/api/v1/ejemplo_envio_fichero/`, formData, {
headers,
responseType: 'blob' as 'json'
}).pipe(
catchError((err) => {
console.log('error caught in service')
console.error(err);
return throwError(err);
})
);
}
En este caso es un poco especial, porque envío un fichero y espero cómo respuesta espero otro modificado. Esta respuesta la quiero devolver directamente al usuario, por lo que la respuesta debe ser un observable. Además en la petición he añadido que responseType: 'blob' as 'json', es decir, devolveré un fichero en formato Json. Esta respuesta la puedo devolver al usuario con el siguiente código:
'''
Función para obtener un fichero de una API REST y devolverlo
'''
class DescargarFichero(Resource):
def get(self):
try:
path = os.path.abspath(os.getcwd())+ '/ejemplo.pdf'
return send_file(path)
except Exception as error:
response = jsonify({'status': 'error'})
response.status_code = 422
return response
api.add_resource(DescargarFichero, '/descargar_fichero')
La forma correcta de obtener el fichero de este método seria con un servicio.ts cómo el siguiente:
/*
Cómunicación con una API de ejemplo para obtener un pdf.
return : Observable<Blob>
*/
descargar_fichero () {
const httpOptions = new HttpHeaders({
'Content-Type': 'application/pdf',
Accept : 'application/pdf'});
return this.http.get<Blob>( api_url,
{headers: httpOptions, responseType: 'blob' as 'json' } )
;
}
A continuación os proporciono un botón de ejemplo para descargar un fichero pdf. Qué tiene la siguiente configuración en la vista: