How should I modify the response to an HTTP request and easily access it before return it out from Observable?

被刻印的时光 ゝ 提交于 2021-01-20 15:35:00

问题


I'm upgrading to Angular to version 5, I was using @angular/http before and now I need to update to @angular/common/http and use HttpClient

I already just make http requests in services (not in components) and that makes them easy to reuse

This is what I already have (from deprecated http)

return this.http.get(url, {headers: this.headers}).map(
      (response: Response) => {
        const data = response.json();
        // Does something on data.data
        
        // return the modified data:
        return data.data;
      }
    ).catch(
      (error: Response) => {
        return Observable.throw(error);
      }
    );

Now from what I learned from new HttpClient it's like I can't modify the response and give it to the component method which is subscribing to it.

how should I modify the response to an HTTP request and easily access it before return it out from Observable?

I just want the same ability to modify the response which I get from API... Like adding some item to it, or do some more stuff to it before returing it


回答1:


It all depends on the version of RxJs. Angular 6 shipped with RxJs 6 - which means that the map()/catch() approach is no longer valid.

Instead, you have to use pipe + map()/catchError() as shown below:

Before Angular 6 / RxJs 6 - classic Http use:

return this.http.get(url, {headers: this.headers}).map(
  (response: Response) => {
    const data : SomeType = response.json() as SomeType;
    // Does something on data.data

    // return the modified data:
    return data.data; // assuming SomeType has a data properties. Following OP post
  }
).catch(
  (error: Response) => {
    throwError(error); // From 'rxjs'
  }
);

Should be changed to this:

After Angular 6 / RxJs 6 - HttpClient migration:

return this.http.get<SomeType>(url, {headers: this.headers})
  .pipe(
     map( response => {  // NOTE: response is of type SomeType
         // Does something on response.data
         // modify the response.data as you see fit.

         // return the modified data:
         return response; // kind of useless
     }),
     catchError( error => {
         return throwError(error); // From 'rxjs'
     })
  ); // end of pipe

In the pipe, map() will pick up the response object (already parsed from JSON) and catchError() will pick up the first error if the HTTP fails.

Also, note that your Headers need to be HttpHeaders object too.

Read on pipe, map and catchError in RxJs 6




回答2:


My service

import {HttpClient} from '@angular/common/http';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
@Injectable()
export class DataService {
    constructor(private http: HttpClient) {}
    getData()
    {
        return this.http.get('../assets/data.json').map(data=>{
            return this.process(data);
        }).catch(
            (error: Response) => {
              return Observable.throw(error);
            });
    }
    process(data:any)  
    {
        let dataTransform:any[]=[];
        let i:number=0;
        for (let item of data)
        {
            dataTransform.push({"id":i,"key":item.key});
            i++;
        }
        return dataTransform;
    }
}

//My component

export class AppComponent implements OnInit {
  constructor(private dataService:DataService){}
  ngOnInit(){
    this.dataService.getData().subscribe((data:any)=> {
      console.log(data);
    });
  }
}

//the asset/data.json

[
    {"key":"uno"},
    {"key":"dos"},
    {"key":"tres"},
    {"key":"cuatro"},
    {"key":"cinco"}
]



回答3:


If you want to handling errors more efficiently, i writting below codes and error classes, please notice to the each part:

  1. I make a folder errors and inside this folder make a class for each of errors, these errors can be 500 errors or 400 errors like blew image:

  1. Make a class app-error.ts like below codes:

    export class AppError {
        constructor(public originalError?: any) { }
    }
    
  2. Another error classes extends from app-error.ts class:

    // not-found-error.ts class
    import {AppError} from './app-error';
    
    export class NotFoundError extends AppError { }
    
    
    
    // conflict-error.ts class
    import {AppError} from './app-error';
    
    export class ConflictError extends AppError { }
    
    
    
    // internal-server-error.ts class
    import {AppError} from './app-error';
    
    export class InternalServerError extends AppError { }
    
    
    
    // bad-request-error.ts class
    import {AppError} from './app-error';
    
    export class BadRequestError extends AppError {
        constructor(public originalError?: any) {
            super(originalError);
        }
    
        get errors(): string[] {
            if (this.originalError)
                return this.originalError;
    
            return null;
        }
    }
    
  3. If you want to access to base error or you can modify error, i do it inside latest class bad-request-error.ts

  4. Then you can use these classes inisde service:

    import {Injectable} from '@angular/core';
    import {HttpClient} from '@angular/common/http';
    import {HttpHeaders} from '@angular/common/http';
    
    import {Observable} from 'rxjs/Observable';
    import 'rxjs/add/operator/map';
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/observable/throw';
    
    import {AppError} from '../errors/app-error';
    import {BadRequestError} from '../errors/bad-request-error';
    import {NotFoundError} from '../errors/not-found-error';
    import {InternalServerError} from '../errors/internal-server-error';
    import {ConflictError} from '../errors/conflict-error';
    
    @Injectable()
    export class DataService {
        public headers = new HttpHeaders().set('Content-Type', 'application/json');
    
        constructor(public http: HttpClient, public url: string) { }
    
        get(id: number) {
            return this.http.get(`${this.url}/${id}`, {headers: this.headers})
                   .map((response) => response.json())
                   .catch(DataService.handleError);
        }
    
        create(resource) {
            return this.http.post(this.url, JSON.stringify(resource), {headers: this.headers})
                   .map((response) => response.json())
                   .catch(DataService.handleError);
        }
    
        update(id: number, resource) {
            return this.http.put(`${this.url}/${id}`, JSON.stringify(resource), {headers: this.headers})
                   .map((response) => response.json())
                   .catch(DataService.handleError);
        }
    
        remove(id: number) {
            return this.http.delete(`${this.url}/${id}`, {headers: this.headers})
                   .map((response) => response.json())
                   .catch(DataService.handleError);
        }
    
        public static handleError(error: Response) {
            switch (error.status) {
                case 400:
                    return Observable.throw(new BadRequestError(error));
                case 404:
                    return Observable.throw(new NotFoundError());
                case 409:
                    return Observable.throw(new ConflictError());
                case 500:
                    return Observable.throw(new InternalServerError());
    
                default:
                    return Observable.throw(new AppError(error));
            }
        }
    }
    
  5. Above code is the best code for error handling and using map operator for manipulating response in success manner.

  6. And the latest pace is using service inside component like below code:

    import {Component} from '@angular/core';
    import {OnInit} from '@angular/core';
    import {HttpParams} from '@angular/common/http';
    
    import {DataService} from '../../services/data.service';
    
    import {AppError} from '../errors/app-error';
    import {BadRequestError} from '../errors/bad-request-error';
    import {NotFoundError} from '../errors/not-found-error';
    import {InternalServerError} from '../errors/internal-server-error';
    import {ConflictError} from '../errors/conflict-error';
    
    @Component({
        selector: 'app-data',
        templateUrl: './data.component.html',
        styleUrls: ['./data.component.css']
    })
    export class DataComponent implements OnInit {
    
        constructor(private dataService: DataService) {
        }
    
        ngOnInit() {
            this.dataService.get(123).subscribe(
                (response: DataModel) => {
                    // ...
                },
                (error: AppError) => {
                    if (error instanceof NotFoundError) {
                        // ...
                    } else if (error instanceof BadRequestError) {
                        // ...
                    } else if (error instanceof ConflictError) {
                        // ...
                    } else {
                        // ...
                    }
                }
            );
        }
    }
    



回答4:


You can create your own Observable that wraps the http.get and return your manipulated response, in this example it is the manipulatedAccountsResponse object:

getAll(): Observable<AccountsResponse> {
  return Observable.create(observer => {
    this.http.get<AccountsResponse>('/accounts')
             .subscribe((result) => {
               const manipulatedAccountsResponse = result;
               // do something with result.
               manipulatedAccountsResponse.setTotal(100);
               observer.next(manipulatedAccountsResponse);
               // call complete if you want to close this stream (like a promise)
               observer.complete();
            });
  });

}




回答5:


As per my consideration you can call function of that service in your success responce and process your data in function and return it to back it.

return this.http.get(url, {headers: this.headers}).map(
      (response: Response) => {
        const data = response.json();
        return this.processData(data);
      }
    ).catch(
      (error: Response) => {
        return Observable.throw(error);
      }
    );
    
public processData(data : any){
  //process your data
  return data;//this is processed data
}



回答6:


In the "new" Httpclient, get return yet a json response by default. Then you only write

import {HttpClient} from '@angular/common/http'; //<--HttpClient
import 'rxjs/add/operator/map';                  //import the operator "map"
import 'rxjs/add/operator/catch'; //and the operator "catch"

....
constructor(private http: HttpClient) {} //<--sure HttpClient
...
return this.http.get(url).map(  //yet get "data"
      (data:any) => {
        return this.processData(data);
      }
    ).catch(
      (error: Response) => {
        return Observable.throw(error);
      }
    );

public processData(data : any){
  //process your data
  return data;//this is processed data
}


来源:https://stackoverflow.com/questions/47617169/how-should-i-modify-the-response-to-an-http-request-and-easily-access-it-before

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!