简体   繁体   中英

Refactoring Angular 4 http calls in service and containing errors

I'm not completely up on the rxjs stuff, so this may well be simply a gap in my knowledge.

I want to refactor some of my http stuff out into a service. Currently I have this...

this.http.get<LicenseKeyAndUsageModel>('api/license').subscribe(data => {
    this.licenseKeyAndUsage = data;
}, (error: HttpErrorResponse) => {
    if (error.status !== 404) {
        //omitted for brevity, essentially notifies of unknown error.
    }
});

To clarify, if 404 is returned, it means there is no existing license key, which is fine as my local variable is never set and everyone's happy.

However, now I want to refactor it out into a service. Most examples I see show returning the 'get' and let the caller subscribe to the result.

return this.http.get<LicenseKeyAndUsageModel>('api/license');

Sounds good.

What I don't understand is how to contain the error within the service so the caller either gets a value, or null. Therefore I can simply assign the result to a local variable again, and let data binding do the rest.

I have seen some examples for similar stuff suggesting passing a callback in, to map into the subscribe CompletionObserver, but that doesn't smell good to me.

I suspect the answer might be in using map and catch, but I can't use the HttpErrorResponse with catch which seems a shame...

return this.http.get<LicenseKeyAndUsageModel>('api/license')
      .map(data => { return data; })
      .catch((error: HttpErrorResponse) => {

      });

(.catch line is not allowed, incorrect signature)

Waiting for the obvious answer :)

The reason you receive a TypeScript error about an incorrect signature is because your implementation of catch needs to return a new Observable . With what you've shown, the problem is actually your void return type.

In order to rethrow the error, you can use Observable.throw , eg:

return Observable.throw(err);

Otherwise, when you want to ignore the error, you can use something like Observable.empty , eg:

return Observable.empty();

To use Observable.empty , you might need to add the following:

import 'rxjs/add/observable/empty';

If you want to return something different when using catch , you can use Observable.of . eg:

return Observable.of(null); // Or...
return Observable.of(/* Create your own LicenseKeyAndUsageModel here */);

The value used in Observable.of will be passed on through, shielding your subscribers from the fact that an error even occurred in the first place.

And again, to use Observable.of , you might need to add the following:

import 'rxjs/add/observable/of';

for error handling if I understand your question correctly you could do this.

whatever your method is called, on subscription do the following.

const myResponse = someHttpService.getLicenseAndModel().subscribe(result => {
  return result,
  return error,
  return complete});

Using .catch is perfectly good solution. What you miss here is that error is of type any . This is why typescript complains about incorrect signature.

return this.http.get('api/license')
  //.map(data => { return data; }) //this is not needed, 
  // you may transform JSON string to actual object here:
  .map(response => response.json())
  .catch((error: any) => {
    // Be aware, that error may be something different than HttpResponse (for example instance of Error or even string).
    // For example, if .json() above would fail, you will get instance of Error class (the one built in JS engine).
    // You may cast error to desired class here.
    if(error instanceof HttpResponse && (error as HttpResponse).status == 404) {
      // Handle this error as you wish here.
    }
    return Observable.throw(error);
  });

Please also remember that Http service returns cold Observable . This means, that HTTP request is not made until you subscribe to returned observable.

References : HttpResponse docs: https://angular.io/api/common/http/HttpResponse

The way how .catch operator is added: https://github.com/ReactiveX/rxjs/blob/master/src/add/operator/catch.ts#L10

Actual definition of .catch : https://github.com/ReactiveX/rxjs/blob/master/src/operator/catch.ts#L64

Honestly, you may just want to change your design. A 404 error typically doesn't mean the server didn't find data, it means you are unauthorized to access the given url. Why wouldn't you simply return null?

Maybe I'm not fully understanding your question?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM