Manage .NET Microservices APIs with Apache APISIX API Gateway
Tags: dotnet, api, docker, microservices
Published: 2022-07-04 11:18:04

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**](https://apisix.apache.org/) for applications developed in **[ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/?view=aspnetcore-6.0)** (Assume that you have an API that manage products) and provide an easy example of how to deploy a multiple images using [docker compose](https://docs.docker.com/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](https://microservices.io/patterns/microservices.html), 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ttea8wp0wd57ha02foxp.png)
 
There are other numerous aspects of an [API Gateway](https://wikitech.wikimedia.org/wiki/API_Gateway) in building [.NET](https://dotnet.microsoft.com/en-us/) microservices APIs and web applications. In many scenarios, _authentication, security, observability, caching, transformation_ are handled centrally. Without an [API Gateway](https://apisix.apache.org/docs/apisix/getting-started) 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](https://microservices.io/patterns/apigateway.html) in reducing complexity, delivering high performance for your APIs and it help you to scale your [microservices](https://en.wikipedia.org/wiki/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:

➡️ [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download)
➡️ [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) with the Web Development, and/or .NET cross-platform development workload installed. This includes by default .NET 6 development tools. Or [Visual Studio code](https://code.visualstudio.com/). 
➡️ [Docker Desktop](https://docs.docker.com/desktop/windows/install/) - you need also [Docker desktop](https://www.docker.com/products/docker-desktop/) installed locally to complete this tutorial. It is available for [Windows](https://desktop.docker.com/win/edge/Docker%20Desktop%20Installer.exe) or [macOS](https://desktop.docker.com/mac/edge/Docker.dmg). Or install the [Docker ACI Integration CLI for Linux](https://docs.docker.com/engine/context/aci-integration/#install-the-docker-aci-integration-cli-on-linux). 

## Here, is a short summary of what we do:

✅ Clone the demo project [apisix-dotnet-docker](https://github.com/Boburmirzo/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](https://github.com/Boburmirzo/apisix-dotnet-docker) I prepared in advance. You can see the complete source code on Github. 

Use [git](https://git-scm.com/downloads) to clone the repository:

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

Go to root directory of _apisix-dotnet-docker_

``` bash
cd apisix-dotnet-docker
```

You can open the project in your favorite code editor. I used [VS Code](https://visualstudio.microsoft.com/downloads/). You’ll see the following project directory structure:

![apisix-dotnet-docker project folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dmi6eaktmmh2srwanf2.png)
 
## 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](https://github.com/Boburmirzo/apisix-dotnet-docker/blob/main/apisix_conf/config.yaml) defines the configs for apisix. Similarly, configs for etcd, prometheus, and grafana are located in [/etcd_conf/etcd.conf.yml](https://github.com/Boburmirzo/apisix-dotnet-docker/blob/main/etcd_conf/etcd.conf.yml), [/prometheus_conf/prometheus.yml](https://github.com/Boburmirzo/apisix-dotnet-docker/blob/main/prometheus_conf/prometheus.yml), and [/grafana_conf/config](https://github.com/Boburmirzo/apisix-dotnet-docker/tree/main/grafana_conf) 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](https://github.com/Boburmirzo/apisix-dotnet-docker/tree/main/apisix_log) folder, APISIX Admin and Control API requests are logged such as `access.log `or `error.log` when APISIX is running.
- **ASP.NET WEB API** - Next, the [ProductApi](https://github.com/Boburmirzo/apisix-dotnet-docker/tree/main/ProductApi) folder keeps basically ASP.NET Core application with _Controller, Domain, and Service classes_). 

![.NET application folder](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qttby6cmjfa3ceykt8vl.png)

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.

``` C#
    [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.

``` script
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](https://github.com/Boburmirzo/apisix-security-java-spring/blob/main/docker-compose.yml) file defines an application with some services: 

 - `apisix-dashboard` - It runs [APISIX Dashboard](https://apisix.apache.org/docs/dashboard/USER_GUIDE) 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](https://apisix.apache.org/docs/apisix/plugins/prometheus).
 - `grafana` - Metrics exported by the [prometheus plugin]https://apisix.apache.org/docs/apisix/plugins/prometheus 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](https://github.com/apache/apisix-docker/tree/master/example).

## 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:

``` bash
docker compose up -d
```

Sample output:

``` bash
[+] 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:


``` bash
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arxo86mr9emolgfl25ut.png)
 

## 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](https://apisix.apache.org/docs/).

Apache APISIX is based on a couple of primitives:

- One of them is the [**Route**](https://apisix.apache.org/docs/apisix/terminology/route)
- Another is the [**Upstream**](https://apisix.apache.org/docs/apisix/terminology/upstream)

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:

``` bash
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`.

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

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

``` bash
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](https://apisix.apache.org/docs/apisix/plugins/limit-count/) 🔌 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:

``` bash
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:

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

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

``` bash
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](https://github.com/apache/apisix-docker) 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](https://apisix.apache.org/plugins). 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. 

## Recommended content 💁

➔ Watch Video Tutorial [Getting Started with Apache APISIX](https://youtu.be/dUOjJkb61so).

➔ Read the blog post [Overview of Apache APISIX API Gateway Plugins](https://dev.to/apisix/overview-of-apache-apisix-api-gateway-plugins-2m8o).

➔ Read the blog post [Run Apache APISIX on Microsoft Azure Container Instance](https://dev.to/apisix/run-apache-apisix-on-microsoft-azure-container-instance-1gdk).

➔ Read the blog post [API Security with OIDC by using Apache APISIX and Microsoft Azure AD](https://dev.to/apisix/api-security-with-oidc-by-using-apache-apisix-and-microsoft-azure-ad-50h3).

➔ Read the blog post [API Observability with Apache APISIX Plugins](https://dev.to/apisix/apis-observability-with-apache-apisix-plugins-1bnm).


Community⤵️

🙋 [Join the Apache APISIX Community](https://apisix.apache.org/docs/general/community) 
🐦 [Follow us on Twitter](https://twitter.com/ApacheAPISIX)
📝 [Find us on Slack](https://join.slack.com/t/the-asf/shared_invite/zt-vlfbf7ch-HkbNHiU_uDlcH_RvaHv9gQ)
📧 [Mail to us](dev@apisix.apache.org) with your questions.

### About the author

Visit my personal blog: [www.iambobur.com](https://www.iambobur.com/)