Create a TypeScript class to invoke ASP.NET Core Web API
In the previous part
of this series you learned about TypeScript functions. Now it's time to peek
into some more interesting features. To that end this part discuses three of
them - interfaces and classes. You will build a fully functional
class that helps you invoke an ASP.NET Core Web API. You then perform CRUD operations
using the class developed in this exercise.
The following page shows the said class in action.
At the top there are five buttons. The click event handlers of these buttons
invoke the respective action of Employees Web API. For example, clicking
the Get button invokes the Get() action of Web API and displays a list of
employees in a table.
This example uses a TypeScript enum, TypeScript interfaces, and TypeScript
classes to accomplish the task. So, let's get going.
Begin by adding a new TypeScript file named Classes.ts in the TypeScript
folder.
Then add the HttpVerbs enumeration in the file as shown below:
enum HttpVerbs {
GET = "GET",
POST = "POST",
PUT = "PUT",
DELETE = "DELETE"
}
The HttpVerbs string enumeration holds items that represent HTTP verbs such as GET,
POST, PUT, and DELETE. These values are used while invoking a Web API.
We could have jump straight into creating the classes but we would rather
prefer to create a few interfaces first and then the classes will implement the
interfaces. This will also help you learn how to create interfaces in TypeScript.
Take a look at the following interface called IEmployee.
interface IEmployee {
employeeID?: number;
firstName: string;
lastName: string;
title: string;
}
The IEmployee interface has four members namely employeID, firstName,
lastName, and title. An object that implements IEmployee interface is requires
to have its members. In this case employeeID is suffixed with ? indicating that
it's an optional member. But the other members are mandatory. So, an object
implementing IEmployee must contain firstName, lastName, and title properties
and optionally it can also contain employeeID property.
Just to make this clear here is an example:
let emp : IEmployee;
emp = {
employeeID:1,
firstName:"Nancy",
lastName:"Davolio",
title : "Sales Executive"
}
And this is also alright:
let emp : IEmployee;
emp = {
firstName:"Nancy",
lastName:"Davolio",
title : "Sales Executive"
}
Interfaces are not limited to property definitions. They can also contain
function declarations. An object implementing that interface is then required to
have those functions.
Let's define an interface called IEmployeeApiClient that dictates how a class
acting as a Web API client should look like:
interface IEmployeeApiClient {
get(callback:(data: IEmployee[])
=> void): void;
getByID(id: number, callback:
(data: IEmployee) => void): void;
post(data: IEmployee, callback:
(msg: string) => void);
put(data: IEmployee, callback:
(msg: string) => void);
delete(id: number, callback:
(msg: string) => void);
}
Notice the IEmployeeApiClient interface carefully. It contains five function
signatures - get(), getByID(), post(), put(), and delete().
The get() function implementation is supposed to invoke the Get() action of
the Employees Web API. The Employees Web API returns an array of employee
objects. The get() function receives a callback function as a parameter. After
invoking the Get() Web API action, the employees returned by the Web API are (IEmployee
array) passed to this callback function. The callback function then displays
them in a table. This arrangement separates Web API invoking logic from results
displaying logic.
The getByID() function is similar but also accepts employee ID through
its id parameter. The callback passed to the getByID() receives a single
IEmployee object.
The post() and put() functions take an IEmployee as a paremeter indicating an
employee to be added or modified respectively. In this case, the callback
receives a success / error message from the Web API.
The delete() function takes an employee ID to be deleted and after deleting
it invokes the callback function by passing the success / error message received
from the Web API.
All the five functions discussed above return void.
From the IEmployeeApiClient interface you know that we are going to need a
set of callback functions that act on the data returned by the Employees Web
API. These callback functions are wrapped inside a TypeScript class discussed
later in this article. To dictate the structure of this class we create another
interface - IEmployeeAppUi. This interface is shown below:
interface IEmployeeAppUi {
getCallback(data: IEmployee[]): void;
getByIDCallback(data: IEmployee): void;
postCallback(msg: string): void;
putCallback(msg: string): void;
deleteCallback(msg: string): void;
}
The IEmployeeAppUi interface contains five methods namely getCallback(),
getByIDCallback(), postCallback(), putCallback(), and deleteCallback(). The
function signatures of these functions should look familiar to you since we
discussed them along with IEmployeeApiClient interface.
So far so good. Now it's time to implement these interfaces in two classes
namely EmployeeApiClient and EmployeeAppUi.
The skeleton of EmployeeApiClient class looks like this:
class EmployeeApiClient implements
IEmployeeApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
...
...
}
The EmployeeApiClient class implements IEmployeeApiClient interface. Inside,
we declare a private member named baseUrl. The baseUrl holds the base address of
the Web API such as http://localhost:12345/Employees. TypeScript provides access
modifiers such as private and public similar to C#. If you don't specify any
access modifier by default the member is public. Here, we don't want baseUrl to
be accessed outside of EmployeeApiClient and hence mark it private.
The EmployeeApiClient class has a constructor as indicated by a function
named constructor. The constructor function accepts a baseUrl value and stores
it in the private baseUrl property declared earlier.
We will now implement IEmployeeApiClient by adding various functions
one-by-one.
The following code shows the implementation of get() function:
get(callback: (data: IEmployee[]) => void):
void {
this.callWebApi(this.baseUrl,
HttpVerbs.GET, callback);
}
The get() function invokes a helper function called callWebApi() that makes a
Web API call. The callWebApi() function as well as the Employees Web API is
discussed later in this article. The callWebApi() function takes three
parameters. The first parameter is the URL of the end-point of the Web API such
as http://localhost:12345/Employees. The second parameter is the HTTP verb to be
used. This parameter is the HttpVerb enumeration and here we pass GET. The third
parameter is a callback function to be invoked once the Web API returns its
response.
Let's take a look at the getByID() implementation:
getByID(id: number,
callback: (data: IEmployee) => void): void {
this.callWebApi(`${this.baseUrl}/${id}`,
HttpVerbs.GET, callback, null, true);
}
The getByID() function also calls the callWebApi() helper function. Notice
that we pass employee ID in the URL parameter after appending it to the baseUrl.
In addition to HttpVerb and callback parameters two more parameters are passed.
The fourth parameter is a object holding employee data used during POST and PUT
operations. Here we pass it as null since GET operations don't need it. The
fifth parameter is a boolean parameter indicating whether the operation is get
"all" or get "by ID". The value of true indicates that it's a get by ID
operation.
The post() function is shown below:
post(data: IEmployee, callback:
(msg: string) => void): void {
this.callWebApi(this.baseUrl,
HttpVerbs.POST, callback,
data);
}
The post() function takes two parameters - an employee object containing
details of a new employee to be added. This object implements IEmployee. And a
callback function. Inside, we call callWebApi() helper function as before. This
time the HTTP verb used is POST and data object is the object holding employee
details.
The put() function is similar to post() but uses PUT verb. The put() function
is shown below:
put(data: IEmployee, callback:
(msg: string) => void): void {
this.callWebApi
(`${this.baseUrl}/${data.employeeID}`,
HttpVerbs.PUT, callback, data);
}
Notice that the URL includes the employeeID value indicating the employee ID
being modified.
The delete() function deletes an employee and is shown below:
delete(id: number, callback:
(msg: string) => void): void {
this.callWebApi
(`${this.baseUrl}/${id}`,
HttpVerbs.DELETE, callback);
}
The delete() function accepts employee ID and callback. Inside, callWebApi()
is called by passing URL and HttpVerb.DELETE.
Now that all the functions are implemented, it's time to add the callWebApi()
helper function.
private callWebApi(url: string,
verb: HttpVerbs,
callback: any,
data: IEmployee = null,
isgetbyid: boolean = false): void {
let xhr = new XMLHttpRequest();
xhr.onload = function () {
if (verb == HttpVerbs.GET) {
if (isgetbyid) {
let retValue: IEmployee;
retValue =
<IEmployee>JSON.parse(xhr.responseText);
callback(retValue);
}
else {
let retValue: IEmployee[];
retValue =
<IEmployee[]>JSON.parse(xhr.responseText);
callback(retValue);
}
}
else {
let retValue: string;
retValue = xhr.responseText;
callback(retValue);
}
}
xhr.onerror = function () {
alert("Error while calling Web API");
}
xhr.open(verb, url);
xhr.setRequestHeader("Content-Type",
"application/json");
if (data == null) {
xhr.send();
}
else {
xhr.send(JSON.stringify(data));
}
}
The callWebApi() function is bit lengthy. So, take a look carefully.
There are five parameters. The url, verb, and callback parameters are
required whereas data and isgetbyid are optional parameters.
Inside, the code creates an XMLHttpRequest object that is used to make Ajax
request to the Employees Web API.
Notice the lines marked in bold letters. They indicate the methods and
properties of the XMLHttpRequest object used by our code. They are discussed
below:
- onload
- onerror
- responseText
- open()
- setRequestHeader()
- send()
The callback function indicated by the onload property is invoked upon
successful completion of the Web API call. The onload function needs to read the
response returned by the Web API. The response will be JSON data for Get() and
GetByID() actions and string for other actions. Moreover, Get() is going to
return JSON array whereas GetByID() is going to return just one employee object
in JSON format. Notice the use of getbyid parameter to figure our which Web API
action is being called.
The code inside the "if" block basically does this checking and accordingly
retValue is either IEmployee[] or IEmployee. In the "else" block retValue is a
string message returned by the Web API.
Notice the use of responseText property of XMLHttpRequest. This property
contains the response sent by the Web API. If the expected response is JSON, we
use JSON.parse() method to read it.
Once the response from the Web API is read the code invokes the callback
function by passing the response to it.
The onerror callback function gets invoked when there is any error while
calling the Web API. Here, it simply displays an alert to the user.
Then
open() method of XMLHttpRequest is called by passing HTTP verb and the URL.
The open() method initializes a request. The setRequestHeader() method sets the
request Content-Type header to application/json because we want JSON to be the
data format.
Finally,
send() method of XMLHttpRequest is used to make the Web API request. You can
also send data along with the request being made. In this example, POST and PUT
requests carry an employee object along with the request body whereas GET and
DELETE don't carry any data.
This completes the EmployeeApiClient class. But you won't be able to compile
it successfully because EmployeeAppUi class and the Employees Web API aren't
ready yet. You will do that in the next part of this series.
That's it for now! Keep coding!!