01 aprilie 2020

Angular - ziua 8 - Cereri HTTP

Cereri HTTP
Angular poate comunica prin intermediul HTTP Requests/Responses catre un Server (API - REST, GraphQL) care la randul sau comunica cu o baza de date.
Documentatia oficiala aici.

Cererea HTTP:
- URL
- HTTP verb: POST, GET, PUT
- Headers (metadata): {"Content-Type" : "application/json"}
- Body: {title : "New Post"}

Solutie de serviciu backend:  Firebase de la Google
- Realtime database (start in test mode)
- URL-ul furnizat poate fi URL-ul la care se pot trimite cereri din Angular

Pasi pentru a folosi HttpClient:
1. app.module.ts -> imports -> HttpClientModule

2. HttpClient se injecteaza in constructorul clasei unde dorim sa-l folosim

3. Trimiterea unei cereri de POST: this.httpClient.post(url: String, postData: object); 
// in functie de url, Firebase va aseza datele intr-o structura de dosare
// postData va fi convertit automat intr-un json

!!! post() intoarce un Observable. Simpla chemare a lui post() nu va declansa nimic, dupa principiul: daca nimeni nu este interesat (= subscription), nu se executa. Asadar:

this.httpClient.post(url: String, postData: object).subscribe(responseData => {
  // do something
});
// nu este nevoie de unsubscribe() pentru ca este un Observable administrat de Angular


GET
this.httpClient.get(url).subscribe(posts => {
  // do something
});


Prelucrarea datelor:

this.httpClient
  .get(url)
  .pipe(map(post => {
    // prelucreaza & return data
  }))
  .subscribe(posts => {
    // do something
  });


Tipizare:

this.httpClient
  .get<{ [key: string]: Post }>(url)
  .pipe(map(post => {
    // process & return data
  }))
  .subscribe(posts => {
    // do something
  });

- unde: [key: string] inseamna orice proprietate cu rol de cheie (nu ii cunoastem numele, este generata la intamplare de Firebase), iar Post este tipul valorii furnizate de aceasta bucata de date
- tipizarea este valabila si pentru alte metode, cum ar fi post()

Se recomanda ca actiunile Http sa se realizeze in servicii.
- fie adnotate cu @Injectable({providedIn: 'root'})
- fie se inscriu in app.module.ts -> providers
- se poate face subscribe concomitent in serviciu (pentru ca actiunea sa se execute), si in componenta care cheama serviciul (doar daca o intereseaza raspunsul)


DELETE
this.httpClient.delete(url, object).subscribe( () => { // empty response
  // here you can set up some flags
});

!!! Pentru separarea intre componenta/serviciu, in serviciu se face delete (fara subscribe), iar in componenta se menajeaza variabilele.


Tratarea erorilor:

error: String;

// ...

this.httpClient.get(url).subscribe(posts => {
  // do something
  this.error = null;
}, error => {
  this.error = error.message;
});

Alt mod de a trata erorile, in serviciu:

error = new Subject<string>();

this.httpClient.post(url, postData).subscribe(responseData => {
  // ..
}, error => {
  this.error.next(error.message);
});

In componenta, in ngOnInit():
this.service.error.subscribe(errorMessage -> {
  this.error = errorMessage;
});

// in acest caz se cuvine sa faci unsubscribe, in ngOnDestroy()


Operatorul catchError:

this.httpClient
  .get(url)
  .pipe(map(post => {
    // prelucreaza & return data
  }), catchError(errorRes => {
    // generic error handling task, like logging / analytics
    throwError(errorRes); // it needs to be able to reach subscribe again
  }))
  .subscribe(posts => {
    // do something
  });


Antete, query params
this.httpClient.get(url, {
  headers: new HttpHeaders({'Custom-Header': 'Hello'}),
  params: new HttpParams().set('print', 'pretty') // echivalent cu url/page.json?print=pretty
}); // pt GET antetul trimis va fi al doilea argument

Alternativ, pentru query params:
let searchParams = new HttpParams(); // obiect imutabil
searchParams = searchParams.append('print', 'pretty');
searchParams = searchParams.append('custom', 'key');
// params: searchParams 

Observare:
this.httpClient.post(url, data, {observe: 'response'}); // response, body, events
this.httpClient
  .delete(url, {observe: 'events'})
  .pipe(tap(event => { // tap nu intrerupe felul cum decurge observabilul, primeste doar events
    if (event.type === HttpEventType.Response) { // verifica daca raspunsul s-a primit
      console.log(event.body);
    }
  })); 

Schimbarea tipului de raspuns:
this.httpClient
  .delete(url, {
    observe: 'events',
    responseType: 'json' // text, blob, etc
  })


Interceptori

export class AuthInterceptorService implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    // next e o functie
    console.log('Request is on its way');
    return next.handle(req); // let the request leave the app
  }
}

In app.module.ts -> providers -> [{provide: HTTP_INTERCEPTORS, useClass: AuthInterceptorService, multi: true}]

Manipulare Request (care este imutabil):
const modifiedRequest = req.clone({
  headers: req.headers.append('Auth': 'xyz')
});
return next.handle(modifiedRequest);

Interceptori pentru raspuns - in return se manipuleaza raspunsul:
return next.handle(req).pipe(tap(event => {
  if (event.type === HttpEventType.Response) {
    // console.log (event.body);
  }
}));

Niciun comentariu: