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.
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:
➡️ .NET 6 SDK
➡️ Visual Studio 2022 with the Web Development, and/or .NET cross-platform development workload installed. This includes by default .NET 6 development tools. Or Visual Studio code.
➡️ Docker Desktop - you need also Docker desktop installed locally to complete this tutorial. It is available for Windows or macOS. Or install the Docker ACI Integration CLI for Linux.
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:
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. You’ll see the following project directory structure:
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
anddashboard_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.log
orerror.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).
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 file defines an application with some services:
-
apisix-dashboard
- It runs APISIX Dashboard on port9000
. ℹ️ 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 port9080
. On your local machine, you can access APISIX Admin API by sending requests to the following URLhttp://127.0.0.1:9080/
-
etcd
- APISIX usesetcd
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]https://apisix.apache.org/docs/apisix/plugins/prometheus can be also graphed in Grafana and you can see the running dashboard on port3000
. -
ProductApi
- While deploying the .NET application, docker compose maps port5555
of the backend service container to port80
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:
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:
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:
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 🔌 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 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.
Recommended content 💁
➔ Watch Video Tutorial Getting Started with Apache APISIX.
➔ Read the blog post Overview of Apache APISIX API Gateway Plugins.
➔ Read the blog post Run Apache APISIX on Microsoft Azure Container Instance.
➔ Read the blog post API Security with OIDC by using Apache APISIX and Microsoft Azure AD.
➔ Read the blog post API Observability with Apache APISIX Plugins.
Community⤵️
🙋 Join the Apache APISIX Community
🐦 Follow us on Twitter
📝 Find us on Slack
📧 Mail to us with your questions.
About the author
Visit my personal blog: www.iambobur.com
Top comments (1)
I first saw this concept of one endpoint with internal routing based on content many years ago. I was hired after it had been created. I guess it could have worked well but didn't see any reason to avoid the simplicity of api routing and established standards. In the end we tore that implementation out.