Create Gateway Service with .NET
15 MINUTE EXERCISE
In this lab you will learn about .NET and how you can build microservices using ASP.NET principles. During this lab you will create a scalable API Gateway that aggregates Catalog and Inventory APIs.
.NET Gateway Project
The gateway-dotnet project has the following structure which shows the components of the project laid out in different subdirectories according to ASP .NET best practices:
This is a minimal ASP .NET project with support for asynchronous REST services.
Examine 'Startup.cs' class
in the /projects/workshop/labs/gateway-dotnet/ directory.
See how the basic web server is started with minimal services, health checks and a basic REST controller is deployed.
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddControllers().AddJsonOptions(options=>
{
options.JsonSerializerOptions.IgnoreNullValues = true;
});
services.AddHealthChecks();
services.AddControllersWithViews();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
ProductsController.Config();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(builder => builder
.AllowAnyOrigin ()
.AllowAnyHeader ()
.AllowAnyMethod ());
app.UseHealthChecks("/health");
app.UseRouting();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Examine 'ProductsController.cs' class
in the /projects/workshop/labs/gateway-dotnet/Controllers directory.
[ApiController]
[Route("api/[controller]")] (1)
public class ProductsController : ControllerBase
{
[HttpGet]
public IEnumerable<Products> Get()
{
private static HttpClient catalogHttpClient = new HttpClient(); (4)
private static HttpClient inventoryHttpClient = new HttpClient();
try
{
// get the product list
IEnumerable<Products> productsList = GetCatalog(); (2)
// update each item with their inventory value
foreach(Products p in productsList) (3)
{
Inventory inv = GetInventory(p.ItemId);
if (inv != null)
p.Availability = new Availability(inv);
}
return productsList;
}
catch(Exception e)
{
Console.WriteLine("Using Catalog service: " + catalogApiHost + " and Inventory service: " + inventoryApiHost);
Console.WriteLine("Failure to get service data: " + e.Message);
// on failures return error
throw e;
}
}
private IEnumerable<Products> GetCatalog()
{
var data = catalogHttpClient.GetStringAsync("/api/catalog").Result;
return JsonConvert.DeserializeObject<IEnumerable<Products>>(data);
}
private Inventory GetInventory(string itemId)
{
var data = inventoryHttpClient.GetStringAsync("/api/inventory/" + itemId).Result;
return JsonConvert.DeserializeObject<Inventory>(data);
}
}
1 | Not unlike the Quarkus and Spring boot apps previously built, the ProductsController has a single defined REST entrypoint for GET /api/products |
2 | In this case the Get() service first requests a list of products from the Catalog microservice |
3 | It then steps through each in turn to discover the amount of product in stock. It does this by calling the Inventory service for each product. |
4 | By using an HttpClient class for each service, .NET will efficiently manage the connection handling. |
The location or binding to the existing Catalog and Inventory REST services is determined at runtime.
Deploy on OpenShift
It’s time to build and deploy your service on OpenShift.
As you did previously for the Inventory and Catalog services in the earlier chapters, you need to build a new Component and then Deploy it in to the OpenShift cluster
We are still going to use OpenShift S2I, but this time we will invoke it using the OpenShift CLI (oc commands). We will also get S2I to compile the application, create the .NET artefact .dll and then create a container.
There are two ways to get OpenShift S2I to build from source:
-
Point S2I at the git repo of the source code
-
Upload the source code and get S2I to build from that
Since we are exploring the Inner Loop and we might have made code changes locally in the IDE we will use the "Upload" method. There are 4 steps here to get the Gateway service running as you can see from the log output and the CLI steps:
-
Create an S2I Build for the .NET application
-
Start the build by uploading the source
-
Create a new application (a deployment) in OpenShift for the application
-
Expose the application using a Route (so that we can easily test it)
Click on 'Terminal' → 'Run Task…' → 'devfile: Gateway - Build and Deploy Component'
Execute the following commands in the terminal window
cd /projects/workshop/labs/gateway-dotnet
oc new-build dotnet:6.0 --name gateway-coolstore \
--labels=component=gateway \
--env DOTNET_STARTUP_PROJECT=app.csproj --binary=true
oc start-build gateway-coolstore --from-dir=. -w
oc new-app gateway-coolstore:latest --name gateway-coolstore --labels=app=coolstore,app.kubernetes.io/instance=gateway,app.kubernetes.io/part-of=coolstore,app.kubernetes.io/name=gateway,app.openshift.io/runtime=dotnet,component=gateway
oc expose svc gateway-coolstore
To open a terminal window, click on 'Terminal' → 'New Terminal'
|
Once this completes, your application should be up and running. OpenShift runs the different components of the application in one or more pods which are the unit of runtime deployment and consists of the running containers for the project.
Test your Service
In the OpenShift Web Console, from the Developer view,
click on the 'Open URL' icon of the Gateway Service
Your browser will be redirected to your Gateway Service running on OpenShift.
Then click on 'Test it'
. You should have an array of json output like this but with many more products.
Look in the json to see how the Gateway service has combined the product information together with the inventory (quantity) information:
[ {
"itemId" : "329299",
"name" : "Red Fedora",
"desc" : "Official Red Hat Fedora",
"price" : 34.99,
"availability" : {
"quantity" : 35
}
},
...
]
Service discovery
You might be wondering how the Gateway service knows how to contact the Catalog and Inventory services. These values can be injected as environment variables at runtime for example if running the component locally in the IDE, but by default we have hardcoded the name of the OpenShift Services and then use OpenShift local DNS to resolve these names as the application starts. You can see the code that consumes those variables below. This is from ProductsController.cs class in the /projects/workshop/labs/gateway-dotnet/ directory.
public static void Config()
{
try
{
// discover the URL of the services we are going to call
catalogApiHost = "http://" +
GetEnvironmentVariable("COMPONENT_CATALOG_COOLSTORE_HOST",
"catalog-coolstore") + ":" +
GetEnvironmentVariable("COMPONENT_CATALOG_COOLSTORE_PORT",
"8080");
inventoryApiHost = "http://" +
GetEnvironmentVariable("COMPONENT_INVENTORY_COOLSTORE_HOST",
"inventory-coolstore") + ":" +
GetEnvironmentVariable("COMPONENT_INVENTORY_COOLSTORE_PORT",
"8080");
// set up the Http conection pools
inventoryHttpClient.BaseAddress = new Uri(inventoryApiHost);
catalogHttpClient.BaseAddress = new Uri(catalogApiHost);
}
catch(Exception e)
{
Console.WriteLine("Checking catalog api URL " + catalogApiHost);
Console.WriteLine("Checking inventory api URL " + inventoryApiHost);
Console.WriteLine("Failure to build location URLs for Catalog and Inventory services: " + e.Message);
throw;
}
}
You can try this simple name service discovery for yourself in the Gateway service pod by selecting the Gateway
service and then the running Pod.
You can test the connectivity by selecting the Pod Terminal
and by executing these shell commands in the terminal window:
curl -w "\n" http://inventory-coolstore:8080/api/inventory/329299
curl -w "\n" http://catalog-coolstore:8080/api/catalog
Well done! You are ready to move on to the next lab.