Quede en XSRF Protection https://angular.io/guide/http#security-xsrf-protection
- Setup
- Comunicacion
- GET y parametros generales de todos los requests
- POST
- PUT
- DELETE
- URL parameters y headers
- Error handling
- Interceptors
- Uso sobre requests
- Uso sobre responses
- Error handling en interceptor
- Configuracion en provider
- Orden
- Modificar un request (inmutabilidad del request)
- Cache
- Progress events
- Best practices
Antes que nada tenes que importar el modulo, hay que importarlo despues de BrowserModule
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
BrowserModule,
// import HttpClientModule after BrowserModule.
HttpClientModule,
]
})
Despues lo inyectas en tus componentes o servicios
import { HttpClient } from '@angular/common/http';
@Injectable()
export class ConfigService {
constructor(private http: HttpClient) { }
}
Todos los requests se hacen mediante observables. es importante recordar:
- cada vez que subscribis a un observable, este se ejecuta
- podes terminar haciendo varias veces el mismo HTTP request, para evitarlo podes usar behavioural subjects (ver RxJS)
- Si no subscribis, el observable no se ejecuta
- Un HTTP DELETE no parece tener necesidad de leer el valor de retorno, pero si no te subscribis entonces nunca se envia.
Podes hacer una solicitud get:
- Usas la funcion get()
- Deberias tener una interfaz que describa la respuesta del server
- Deberias colocarla como generic en la funcion get() para que typescript pueda hacer typechecking en la respuesta del server
sintaxis:
http.get<MiInterfaz>('Mi url donde hacer la solicitud');
Parametros:
get(
url: string,
options: {
headers:{
NombreDeHeader: valor
}
// si el valor es 'response' obtenes todo el response, no solo JSON
observe?: HttpObserve;
//Parametros/querys a enviar
params?: {
nombreDeParametro:valor
};
reportProgress?: boolean;
responseType?: 'arraybuffer' | 'blob' | 'json' | 'text';
//Determina si se enviaran credenciales
withCredentials?: boolean;
};
}): Observable<any>
Ejemplo:
// esta interfaz permite a typescript determinar como se vera la respuesta del server
// y hacer typechecking
interface misDatos {
nombre: string,
apellido: string,
}
constructor(private http: HttpClient) {
// Coloco misDatos como generic para poder
this.http.get<misDatos>('/miserver/misdatos.json');
}
Tiene los mismos parametros que GET pero ademas acepta algo que enviar. principalmente un js object que envias como json
return this.http.post<MiInterfaz>(URL, objetoEnviado, httpOptions)
.pipe(
catchError(this.handleError('addHero', hero))
);
return this.http.delete(url, httpOptions)
.pipe(
catchError(this.handleError('deleteHero'))
);
return this.http.put<Hero>(this.heroesUrl, hero, httpOptions)
.pipe(
catchError(this.handleError('updateHero', hero))
);
Podes añadir parametros ó headers bien encodeados haces asi:
misParametros = new HttpParams()
.set('nombreDeParametro1', Valor1)
.set('nombreDeParametro2', Valor2)
misHeaders = new HttpHeaders(
{
'Content-Type': 'application/json',
'Authorization': 'my-auth-token'
})
this.http.get<Hero[]>(this.heroesUrl, {params: misParametros, headers: misHeaders })
si hay un error entonces el Observer debera estar en condiciones de manejarlo.recibira un error object. esto es algo que queremos hacer en un servicio
Esto es algo propio de los Observables, si no te acordas anda a mirar el documento de RxJS
this.miServicio.obtenerDatos()
.subscribe(
data => this.config = { ...data }, // success path
error => this.error = error // error handling
);
Podes usar el RxJS operator Retry() para reintentar un par de veces un request.
return this.http.get<Config>(URL)
.pipe(
retry(3), // retry a failed request up to 3 times
catchError(this.handleError) // then handle the error
);
Te permite interceptar una serie de requests http, muchas veces se usa para no tener que implementar autenticacion en cada get/post. para esto hay que implementar la funcion interept() de la interfaz HttpInterceptor
acordate que esta bueno tener una interfaz que te deterine el type de la respuesta esperada, en este caso la simbolizamos como MiInterfaz
Un interceptor:
- Puede pasarle el request al siguiente interceptor
- Puede finjir devolver una respuesta del server
- Puede pasarle un request totalmente diferente al siguiente interceptor.
- Devuelven objetos HttpEvent y no HttpResponse
sintaxis
// se importa de
import {HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
//acordate que tenes que IMPLEMENTAR intercept, NO llamarla
interept(miRequestInterceptada<MiInterfaz>, SiguienteHandler){
// hacer cosas
//Opcion 1: pasar el request al siguiente handler
return next.handle(miRequestInterceptada);
//opcion 2: devolver una respuesta "falsa" del server
return Observable.of(algoDeTipoHTTPREQUEST)
// Opcion 3: devolver OTRA request
//miRequestInterceptada2 se envia, miRequestInterceptada se pierde
miRequestInterceptada2 = miRequestInterceptada.clone()
return next.handle(miRequestInterceptada2);
}
Ejemplo:
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
/** Pass untouched request through to the next request handler. */
@Injectable()
export class miInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<MiInterfaz>, next: HttpHandler):Observable<HttpEvent<any>> {
//**tu proceso aca**
//Pasar el request al proximo interceptor, si no hay mas se envia.
return next.handle(req);
}
}
El interceptor usado para modificar la respuesta es el mismo que el que se usa para modificar la request. lo unico que tenes que hacer es usar el observable next.handle(req), lo que coloques como operador despues de el se ejecuta despues de todos los intercepts de salida y de que la request haya salido y vuelto del server y por lo tanto funcionara sobre la respuesta del server
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).map((event: HttpEvent<any>) => {
// Verificas si es una respuesta
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
})
}
Atencion: El error handling podria hacerse en donde estas llamando el http request, esto es lo mas normal!, pero hay casos (Autenticacion por ejemplo) donde es preferible que el interceptor sea el que maneje el error, o que masajee el error para devolver directamente un mensaje de error al observer, esto ademas permite un global error handling
Si algo falla en el observable next.handle(req) entonces podes atajar el error dentro del observer al final del http request.
PERO Si quisieras manejarlo dentro del interceptor, podes! solo tenes que usar el RxJS operator catch() que hace que en lugar de ir a la funcion error del observer, correr una funcion y devolver otro observable
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req)
.catch(err => {
if (err instanceof HttpErrorResponse {
if (err.status === 401) {
// me expiro el token, hace tu logica aca.
// en este caso, hay que implementar que el observer muestre esto
//en pantalla
Observable.throw("la sesion expiro!");
}
}
})
}
Una vez que tenes tu clase que implementa interceptor(), tenes que** proveerla para que pueda ser inyectada**. Esto lo hacemos con un provider.
Internamente los providers actuan como dependencias de HttpClient, asi que hay que definirlos antes de que angular instancia HttpClient
Para añadir interceptors a un array de providers, hay que hacerlo asi:
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor2, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor3, multi: true }
O ponerlos todos en un solo provider
/** en alguna clase: */
export const MisInterceptores = [
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor2, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: miInterceptor3, multi: true }
];
/** en el modulo: */
providers: [ MisInterceptores],
El orden de los interceptors en el array de privders es el orden en el que seran usados.
Si los interceptores son
los requests fluyen:
Los requests son inmutables despues de que son creados, si queres modificar algo en un request que ya existe (que es lo que sucede en un interceptor) tenes que clonarlo, no vas a poder modificar sus propiedades
// Clonar y hacer nuestras modificaciones a la nueva instancia
const secureReq = req.clone({
url: req.url.replace('http://', 'https://')
});
// Pasarle la nueva instancia al handler
return next.handle(secureReq);
Podes cachear respuestas.
const noHeaderReq = req.clone({ headers: new HttpHeaders() });
// le sacas los headers
return next.handle(noHeaderReq).pipe(
tap(event => {
// There may be other events besides the response.
if (event instanceof HttpResponse) {
// guardas en el cache o lo actualizas
cache.put(req, event); // Update the cache.
}
})
y usar las respuestas del cache
intercept(req: HttpRequest<any>, next: HttpHandler) {
// Si la respuesta no es cacheable segun HTTP, seguir de largo
if (!isCachable(req)) { return next.handle(req); }
//Si lo es, intentar obtenerla de nuestro cache
const cachedResponse = this.cache.get(req);
//Si la obtuvimos, usamos esa, si no, enviamos el request tal como lo obtuvimos
return cachedResponse ?
of(cachedResponse) : sendRequest(req, next, this.cache);
}
Mas detalle aca Cuando estas subiendo o bajando archivos podes obtener status reports del browser activando el flag reportProgress
const req = new HttpRequest('POST', '/upload/file', file, {
reportProgress: true
});
//en analizarEvento interpreas el HttpEvent y generas algun mensaje para el usuario
return this.http.request(req).pipe(
map(event => this.analizarEvento(event)),
tap(message => this.mostrarProgreso(message)),
last(), // return last (completed) message to caller
catchError(this.handleError(file))
);
- Separar en un servicios y no en un componentes
- Comunicacion con servidores
- Error handling, interpretation y resolution de las comunicaciones
- Que los HttpRequest tengan una interfaz de la respuesta esperada
- En gets, posts, interceptors, etc