Manage .NET Microservices APIs with Apache APISIX API Gateway

Manage .NET Microservices APIs with Apache APISIX API Gateway

The API Gateway topic could easily carry several blog posts like this one. In this section, we focus specifically on the usage of Apache APISIX API Gateway for applications developed in ASP.NET Core (Assume that you have an API that manage products) and provide an easy example of how to deploy a multiple images using docker compose.

Now before going to the demo session. Let's discuss first what is API gateway?🤔

API Gateway as a single entry point

In today's microservices architecture, we usually create multiple microservices for a particular product and the client apps usually need to consume functionality from more than one microservice. And for each of these services, we will have different endpoints accessing these services from the external world it doesn't make sense to expose multiple URLs we should have a single entry point to all our services, and based on the different paths we should be doing the routing as it is shown in the below picture.

Apache APISIX API Gateway as a single entry point

There are other numerous aspects of an API Gateway in building .NET microservices APIs and web applications. In many scenarios, authentication, security, observability, caching, transformation are handled centrally. Without an API Gateway in place, you might typically implement these concerns for each service, because maintaining them for each service would be a very challenging task and time consuming. At the same time, you can get benefit of an API Gateway in reducing complexity, delivering high performance for your APIs and it help you to scale your microservices.

Prerequisites

👉 To execute and customize the example project per your need shown in this post, here are the minimum requirements you need to install in your system:

Here, is a short summary of what we do:

  • ✅ Clone the demo project apisix-dotnet-docker from GitHub.
  • ✅ Understand the structure of the project and docker-compose.yaml file.
  • ✅ Build a multi-container APISIX via Docker CLI.
  • ✅ Configure APISIX API Gateway routing for the ASP.NET API.
  • ✅ Enable a traffic management plugin.

Clone the demo project

For this demonstration, we’ll leverage the demo project apisix-dotnet-docker I prepared in advance. You can see the complete source code on Github.

Use git to clone the repository:

git clone 'https://github.com/Boburmirzo/apisix-dotnet-docker'

Go to root directory of apisix-dotnet-docker

cd apisix-dotnet-docker

You can open the project in your favorite code editor. I used VS Code. You’ll see the following project directory structure:

apisix-dotnet-docker project folder

Understand the structure of the project

In the project folders, here is the list of main components you can take a look at:

  • APISIX's config files - All the services are configured by mounting external configuration files in the project onto the docker containers: /apisix_conf/conf.yaml defines the configs for apisix. Similarly, configs for etcd, prometheus, and grafana are located in /etcd_conf/etcd.conf.yml, /prometheus_conf/prometheus.yml, and /grafana_conf/config respectively. ☝️Note that grafana_conf, prometheus_conf and dashboard_conf are all optional, only if you would like to use them in your project.
  • APISIX's logs - in the apisix_log folder, APISIX Admin and Control API requests are logged such as access.logor error.log when APISIX is running.
  • ASP.NET WEB API - Next, the ProductApi folder keeps basically ASP.NET Core application with Controller, Domain, and Service classes).

.NET application folder

You can also see ProductsController.cs file where there is a simple API to get all products list from the service layer. We will enable routing and Apache APISIX plugins for this endpoint in the next steps.

    [Route("api/[controller]")]
    [ApiController]
    public class ProductsController : ControllerBase
    {
        private IProductsService _productsService;
        public ProductsController(IProductsService productsService)
        {
            _productsService = productsService;
        }

        [HttpGet]
        public IActionResult GetAll()
        {
            return Ok(_productsService.GetAll());
        }
    }

Now let's have a look at a Dockerfile inside the ProductApi folder. It has just standard Docker file structure and this file is responsible for pulling .NET6 SDK and ASP.NET6 images from docker registry, building and running the applications docker images.

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
WORKDIR /app

COPY . ./
RUN dotnet restore "ProductApi.csproj"
RUN dotnet publish "ProductApi.csproj" -c Release -o out

FROM mcr.microsoft.com/dotnet/aspnet:6.0
ENV TZ=America/Sao_Paulo
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
WORKDIR /app
COPY --from=build-env /app/out .
ENTRYPOINT ["dotnet", "ProductApi.dll"]
  • Docker compose file - The docker-compose.yml file defines an application with some services:

    • apisix-dashboard - It runs APISIX Dashboard on port 9000. ℹ️ The Dashboard offers another way to interact with APISIX Admin API and we can achieve the same configuration result with the CLI as with the Dashboard.

    • apisix- deploys APISIX and exposes it on port 9080. On your local machine, you can access APISIX Admin API by sending requests to the following URL http://127.0.0.1:9080/

    • etcd - APISIX uses etcd to save and synchronize configuration.
    • prometheus - APISIX can fetch metrics data about upstream APIs together with prometheus plugin.
    • grafana - Metrics exported by the [prometheus plugin]apisix.apache.org/docs/apisix/plugins/prome.. can be also graphed in Grafana and you can see the running dashboard on port 3000.
    • ProductApi - While deploying the .NET application, docker compose maps port 5555 of the backend service container to port 80 of the host.

☝️You may notice all services are mapped to apisix network.

The apisix-dotnet-docker project makes use of the similar example APISIX docker compose template.

Build a multi-container APISIX via Docker CLI

Now we can start our application by running docker compose up command from the root folder of the project:

docker-compose up -d

Sample output:

[+] Running 7/7
 - Network apisix-dotnet-docker_apisix                Created                                                              0.0s
 - Container apisix-dotnet-docker-apisix-dashboard-1  Started                                                              1.2s
 - Container apisix-dotnet-docker-prometheus-1        Started                                                              0.7s
 - Container apisix-dotnet-docker-etcd-1              Started                                                              0.9s
 - Container apisix-dotnet-docker-grafana-1           Started                                                              1.2s
 - Container apisix-dotnet-docker-productapi-1        Started                                                              0.7s
 - Container apisix-dotnet-docker-apisix-1            Started                                                              2.0s

The running container list you can see by running docker compose ps CLI command or using docker desktop:

NAME                                      COMMAND                  SERVICE             STATUS              PORTS
apisix-dotnet-docker-apisix-1             "sh -c '/usr/bin/api…"   apisix              running             0.0.0.0:9080->9080/tcp, 0.0.0.0:9091-9092->9091-9092/tcp, 0.0.0.0:9443->9443/tcp
apisix-dotnet-docker-apisix-dashboard-1   "/usr/local/apisix-d…"   apisix-dashboard    running             0.0.0.0:9000->9000/tcp
apisix-dotnet-docker-etcd-1               "/opt/bitnami/script…"   etcd                running             0.0.0.0:12379->2379/tcp
apisix-dotnet-docker-grafana-1            "/run.sh"                grafana             running             0.0.0.0:3000->3000/tcp
apisix-dotnet-docker-productapi-1         "dotnet ProductApi.d…"   productapi          running             0.0.0.0:5555->80/tcp
apisix-dotnet-docker-prometheus-1         "/bin/prometheus --c…"   prometheus          running             0.0.0.0:9090->9090/tcp

Once the containers are running, navigate to http://localhost:5555/api/products in your web browser and you will see the following output:

Product list .NET API response

Configure APISIX API Gateway

When you set up Apache APISIX API Gateway for your APIs, it adds many capabilities including 🔽

  • Routing.
  • Request Aggregation.
  • Authentication.
  • Authorization.
  • Rate Limiting.
  • Caching.
  • Retry policies / QoS.
  • Load Balancing.
  • Logging / Tracing / Metrics.
  • Headers / Method / Query String / gRPC / Claims Transformation.
  • Configuration / Administration REST API.
  • and many more...

For more information see the documentation.

Apache APISIX is based on a couple of primitives:

We can create a Route and configure the underlying Upstream. When Apache APISIX receives a request matching the Route, it forwards it to the underlying Upstream.

Now let's start with adding a Route and Upstream for the /api/products endpoint. The following command creates a sample Route together with an upstream:

curl "http://127.0.0.1:9080/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '    
{
  "methods": ["GET"],                                       
  "uri": "/api/products",                   
  "upstream": {                           
    "type": "roundrobin",
    "nodes": {
      "productapi:80": 1
    }
  }
}'

Once we have created the Route and upstream, we can check whether it works. Apache APISIX should forward the request to our target API /api/products.

curl http://127.0.0.1:9080/api/products -i

Urraaa👏, yes, it actually does. You can see a sample output:

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.13.1

[{"name":"Macbook Pro","price":1299.9},{"name":"SurfaceBook 3","price":1599.9}]

You can also add two or more routes to the API Gateway, let's say one will serve for you Product and another for Customer microservices with the URL path /api/customers.

Enable a traffic management plugin

With the help of the API Gateway, one can set automatic retries, timeouts, circuit breakers, or rate-limiting for an external upstream API or microservice. Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a specific timeframe – for instance, trying to log into an account.

The Limit count plugin 🔌 is one among many limiting plugins. It limits the request rate by a fixed number of requests in a given time window.

Let’s enable the limit-count plugin on the Route and Upstream. To do so, run the following command:

curl -i http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
    "methods": ["GET"],                                       
    "uri": "/api/products",
    "plugins": {
        "limit-count": {
            "count": 2,
            "time_window": 60,
            "rejected_code": 403,
            "rejected_msg": "Requests are too frequent, please try again later.",
            "key_type": "var",
            "key": "remote_addr"
        }
    },
    "upstream": {                           
      "type": "roundrobin",
      "nodes": {
       "productapi:80": 1
     }
   }
}'

The above configuration limits the number of requests to two in 60 seconds. Apache APISIX will handle the first two requests as usual, but a third request in the same period will return a 403 HTTP code:

curl http://127.0.0.1:9080/api/products -i

Sample output after calling the API 3 times within 60 sec:

HTTP/1.1 403 Forbidden
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.13.1

{"error_msg":"Requests are too frequent, please try again later."}

Indeed, after reaching the threshold, the subsequent requests are not allowed by APISIX.

Conclusion

We made use of Apache APISIX docker compose repo where we deployed several components in one run and we demonstrated how the API Gateway can be used to manage sample ASP.NET Core application to retrieve product data using Product microservice's API. Also, you learned how to enable limit-count plugin for the API endpoint. There are many other built-in plugins available in Apache APISIX, you can check them on Plugin Hub page. Basically, APISIX can be a lightweight middleware API Gateway regardless of which programming language, frameworks, tools, or platforms you are developing a microservice or application.

Community⤵️

Did you find this article valuable?

Support Bobur's Blog by becoming a sponsor. Any amount is appreciated!