Welcome to today’s post.
In today’s post I will be discussing how we can determine if a web API service is online or offline.
Why would we need to determine if the web API service is online?
The reason why we would need to determine if the API is online is so that our application can function. Without the API being online we would have no access to application functionality including reading and modifying backend data and calling backend services.
Setup of a Health Service within an Angular Application
I will give a basic technique we can use to determine API status from an Angular application.
First, in our .NET Core backend, I define a basic API method that does nothing but return a status code of 200 (OK). The method is defined below:
[HttpGet("api/[controller]/TestMethod")]
public ActionResult TestMethod()
{
return Ok();
}
In our Angular SPA client, we define a periodic polling observable that then calls the API method in our web API. First, we set a notification polling interval for our health check. We do this as shown:
export class NotificationConfiguration
{
public static HEALTHCHECK_INTERVAL: number = 30000;
}
I have set the interval at 30 seconds for testing purposes. You might set an higher interval in a more realistic environment.
We then implement a basic health check service that calls the API method with an HTTP GET call:
import { Inject, Injectable } from '@angular/core'
import { HttpClient, HttpErrorResponse, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http'
import { Book } from 'src/app/models/Book';
import { environment } from 'src/environments/environment';
import { AppConfig, APP_CONFIG } from '../app.config';
import { Observable } from 'rxjs';
@Injectable()
export class HealthCheckService {
constructor(@Inject(APP_CONFIG) private config: AppConfig, private http: HttpClient) {}
testAPIready(): Observable<HttpResponse<any>> {
const options = {
observe: 'response' as const
};
return this.http.get<any>(this.config.baseApiUrl + '/Book/TestMethod', options);
}
setHealthStatus(state: string) {
localStorage.setItem('apiReady', state);
}
getHealthStatus(): string {
return localStorage.getItem('apiReady');
}
}
Note the response type, HttpResponse gives us the details of the status, statusText and url as shown:
In one of our top-level components, such as the app or menu, we import the health check service in the constructor as shown:
import { HealthCheckService } from '../services/health-check.service';
We inject the health check service in the constructor as shown:
constructor(
public router: Router,
private healthCheckService: HealthCheckService,
private notificationService: NotificationService,
private snackBar: MatSnackBar,
private authService: AuthService) { }
Given we have defined the health check services within our Angular frontend, the next step is to subscribe and poll the web API from within the service, which is what I will show how to implement in the next section.
Polling the Web API Service method from the Health Check Service
We declare a subscription that will allow us to poll and call the API method as shown:
healthCheckSubscription: Subscription;
Next, we setup subscription for our polling as shown:
this.healthCheckSubscription = interval(NotificationConfiguration.HEALTHCHECK_INTERVAL).subscribe(() =>
{
var statusText = 'Polling API Health....';
console.log(statusText);
});
In a previous post, I showed how to use polling to query data periodically from a Web API and output the results using the Material snack bar notification.
When the above is run, it will do nothing but output text periodically.
We then implement a subscription that calls our health check service and elicits a response as shown:
this.healthCheckSubscription =
interval(NotificationConfiguration.HEALTHCHECK_INTERVAL)
.subscribe(() =>
{
var statusText = 'Polling API Health....';
console.log(statusText);
this.healthCheckService
.testAPIready()
.subscribe((val: any) => {
if (val.status === 200) {
statusText = statusText + 'OK';
this.healthCheckService.setHealthStatus("OK");
} else {
statusText = statusText + 'Fail. Status Code = '
+ val.status.toString();
this.healthCheckService.setHealthStatus("ERROR");
}
console.log(statusText);
},
err => {
this.openSnackBar("Polling API service",
"The API service is offline.");
console.log(statusText + 'OFFLINE');
});
});
We should also provide a means to destroy the subscription when the component is destroyed to avoid memory leaks:
ngOnDestroy()
{
if (!this.healthCheckSubscription)
return;
this.healthCheckSubscription.unsubscribe();
}
When the service returns successfully, we expect the usual 200 (OK) response. If the method fails to be executed, then a response other than 200 will be returned. In most cases unless there is additional complexity within the method.
Detection of Web API Status in an IIS Hosted Application
Finally, when the API method cannot be executed, and does not return any response, it will return an error. In this case, we can expect the API service to be offline. Reasons for the API service being offline include:
- The Server hosting the web API service is offline.
- The IIS site hosting the API service is offline.
- The IIS app pool of the API service has shut down.
Below is when we have the app pool of our Web API stopped:
When the Angular client application is run, we can expect the following output responses when the API is online:
Polling API Health....
Polling API Health....OK
In POSTMAN, testing the API when it is online gives us the 200 (OK) response:
When the API is offline the following output response is expected:
Polling API Health....
Polling API Health....OFFLINE
In POSTMAN, testing the API when it is offline gives us the 503 (service unavailable) error:
The console will also include the following error message related to CORS that prevents error messages from showing in the client browser:
Access to XMLHttpRequest at ‘http://localhost/BookLoan.Catalog.API/api/Book/TestMethod’ from origin ‘http://localhost:4200’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. [http://localhost:4200/home]
We will also get a failure to load resource as shown:
Failed to load resource: net::ERR_FAILED [http://localhost/BookLoan.Catalog.API/api/Book/TestMethod]
When we output the err object from the observable, all the get is the generic error message below, which is not helpful or provides any error status codes:
You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
The above is one way to approach the problem of determining API availability. There are other approaches, including developing an external windows or hosted service that polls all API services and writes their status to a separate report data store which is then queried as another API.
The above is quite an efficient way to determine availability without implementing additional services that would be extra overhead.
In a future post I will be exploring ways of determining health checks on backend services that are dependent on an ASP.NET Core Web API service.
That is all for today’s post. I hope you have found this post useful and informative.
Andrew Halil is a blogger, author and software developer with expertise of many areas in the information technology industry including full-stack web and native cloud based development, test driven development and Devops.